Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีใช้ AlarmManager ใน Android

ภาพรวม

ตัวอย่างนี้แสดงวิธีใช้ AlarmManager ตั้ง鬧ลำ (Alarm) แบบซ้ำทุกวัน โดยใช้ TimePicker เลือกเวลา และ BroadcastReceiver รับ Event เมื่อถึงเวลา (โค้ดเป็น Kotlin)

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project (เลือก Kotlin)

ขั้นตอนที่ 2: Layout (res/layout/activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="8dp"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:orientation="vertical">

        <TimePicker
            android:id="@+id/timePicker"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/buttonAlarm"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="Set Alarm"/>

    </LinearLayout>
</RelativeLayout>

ขั้นตอนที่ 3: โค้ดหลัก (src/MainActivity.kt)

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TimePicker
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import java.util.*

class MainActivity : AppCompatActivity() {

    private lateinit var btnSetAlarm: Button
    private lateinit var timePicker: TimePicker

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = "AlarmManager Demo"

        timePicker = findViewById(R.id.timePicker)
        btnSetAlarm = findViewById(R.id.buttonAlarm)

        btnSetAlarm.setOnClickListener {
            val calendar = Calendar.getInstance()

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                // API 23+ ใช้ hour/minute property
                calendar.set(
                    calendar.get(Calendar.YEAR),
                    calendar.get(Calendar.MONTH),
                    calendar.get(Calendar.DAY_OF_MONTH),
                    timePicker.hour,
                    timePicker.minute,
                    0
                )
            } else {
                // API เก่า ใช้ currentHour/currentMinute
                calendar.set(
                    calendar.get(Calendar.YEAR),
                    calendar.get(Calendar.MONTH),
                    calendar.get(Calendar.DAY_OF_MONTH),
                    timePicker.currentHour,
                    timePicker.currentMinute,
                    0
                )
            }

            setAlarm(calendar.timeInMillis)
        }
    }

    private fun setAlarm(timeInMillis: Long) {
        val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val intent = Intent(this, MyAlarm::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            this, 0, intent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )

        // ตั้ง Alarm ซ้ำทุกวัน
        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP,
            timeInMillis,
            AlarmManager.INTERVAL_DAY,
            pendingIntent
        )

        Toast.makeText(this, "Alarm is set for " + formatTime(timeInMillis), Toast.LENGTH_SHORT).show()
    }

    private fun formatTime(millis: Long): String {
        return java.text.SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date(millis))
    }

    // Inner BroadcastReceiver
    class MyAlarm : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Log.d("Alarm Bell", "Alarm just fired")
            Toast.makeText(context, "Alarm!", Toast.LENGTH_SHORT).show()
        }
    }
}

ขั้นตอนที่ 4: Manifest (androidManifest.xml)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="com.example.alarmdemo">
    <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.USE_EXACT_ALARM" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".MainActivity\$MyAlarm"
            android:exported="true"/>

    </application>
</manifest>

การทดสอบ

  1. รันแอป เลือกเวลาใน TimePicker
  2. กด "Set Alarm"
  3. รอจนถึงเวลา (หรือตั้งเวลาใกล้ๆ) - จะแสดง Toast "Alarm!"

ข้อควรระวังสำคัญ (Android 12+ / API 31+)

  • SCHEDULE_EXACT_ALARM และ USE_EXACT_ALARM - ต้องขอสิทธิ์พิเศษสำหรับ Alarm แบบ Exact
  • PendingIntent.FLAG_IMMUTABLE - **บังคับ** ตั้งแต่ Android 12 (API 31+)
  • Doze Mode / App Standby - อาจล่าช้า Alarm ใช้ setAlarmClock() สำหรับ Alarm ที่ต้องแม่นยำ
  • Boot Completed - Alarm จะหายไปหลัง Reboot ต้องตั้งใหม่ใน Boot Receiver

ทางเลือกสมัยใหม่

  • WorkManager - สำหรับงาน Deferrable, Guaranteed execution, รองรับ Constraints (ชาร์จ, WiFi, Idle)
  • JobScheduler (API 21+) - คล้าย WorkManager แต่ Low-level
  • AlarmManager.setExactAndAllowWhileIdle() - ถ้าต้องใช้ AlarmManager อยู่ดี

Java Version (สำหรับโปรเจกต์ Java)

// ใน onCreate
buttonAlarm.setOnClickListener(v -> {
    Calendar calendar = Calendar.getInstance();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        calendar.set(Calendar.HOUR_OF_DAY, timePicker.getHour());
        calendar.set(Calendar.MINUTE, timePicker.getMinute());
    } else {
        calendar.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour());
        calendar.set(Calendar.MINUTE, timePicker.getCurrentMinute());
    }
    calendar.set(Calendar.SECOND, 0);
    setAlarm(calendar.getTimeInMillis());
});

// MyAlarm เป็น Class แยก (public class MyAlarm extends BroadcastReceiver)