Alarm (berdasarkan class AlarmManager
) memberikan cara untuk melakukan
operasi berbasis waktu di luar masa aktif aplikasi Anda.
Misalnya, Anda dapat menggunakan alarm untuk memulai operasi yang berjalan lama, seperti
memulai layanan sekali dalam satu hari untuk mendownload perkiraan cuaca.
Alarm memiliki karakteristik berikut:
- Alarm memungkinkan Anda memicu Intent pada waktu/interval yang ditentukan.
- Anda dapat menggunakannya bersama dengan penerima siaran untuk memulai layanan dan melakukan operasi lain.
- Alarm beroperasi di luar aplikasi Anda, sehingga dapat digunakan untuk memicu peristiwa atau tindakan bahkan saat aplikasi Anda sedang tidak berjalan, dan meskipun perangkat itu sendiri dalam mode tidur.
- Alarm membantu meminimalkan kebutuhan resource aplikasi Anda. Anda dapat menjadwalkan operasi tanpa mengandalkan timer atau menjalankan layanan latar belakang secara terus-menerus.
Catatan: Untuk pemilihan waktu operasi yang dijamin terjadi
selama masa aktif aplikasi Anda,
pertimbangkan untuk menggunakan class Handler
bersama dengan
Timer
dan Thread
. Pendekatan ini memberi Android
kontrol yang lebih baik atas resource sistem.
Memahami konsekuensi
Alarm berulang adalah mekanisme yang relatif sederhana dengan fleksibilitas yang terbatas. Alarm mungkin bukan pilihan terbaik untuk aplikasi Anda, terutama jika Anda perlu memicu operasi jaringan. Alarm yang tidak didesain dengan baik dapat menghabiskan baterai dan memberikan beban berat pada server.
Skenario umum untuk memicu operasi di luar masa aktif aplikasi adalah
saat menyinkronkan data dengan server. Dalam kasus ini Anda mungkin berminat untuk menggunakan
alarm berulang. Namun, jika Anda adalah pemilik dari server yang menghosting data aplikasi
Anda, penggunaan Google Cloud Messaging (GCM)
bersama dengan adaptor sinkronisasi
adalah solusi yang lebih baik dari AlarmManager
. Adaptor sinkronisasi memberi Anda
semua opsi penjadwalan yang sama dengan AlarmManager
, tetapi memberikan
fleksibilitas yang jauh lebih tinggi. Misalnya,
sinkronisasi dapat didasarkan pada pesan "data baru" dari server/perangkat (buka
Menjalankan Adaptor
Sinkronisasi untuk mengetahui detailnya), adanya (atau tidak adanya) aktivitas pengguna, waktu, dan masih banyak lagi.
Buka video yang ditautkan di bagian atas halaman ini untuk mengetahui diskusi mendetail terkait waktu dan
cara penggunaan GCM dan adaptor sinkronisasi.
Alarm tidak akan terpicu saat perangkat menganggur dalam
mode Istirahatkan. Semua alarm yang dijadwalkan akan
ditunda sampai perangkat tidak lagi dalam mode Istirahatkan. Jika Anda perlu memastikan bahwa pekerjaan Anda selesai bahkan saat
perangkat sedang menganggur, ada beberapa opsi yang tersedia. Anda dapat menggunakan
setAndAllowWhileIdle()
atau
setExactAndAllowWhileIdle()
untuk menjamin
bahwa alarm akan dijalankan. Opsi lainnya adalah menggunakan WorkManager API baru, yang dibuat untuk melakukan
pekerjaan latar belakang baik satu kali maupun secara berkala. Untuk mengetahui informasi lebih lanjut, buka
Menjadwalkan tugas dengan WorkManager.
Tips
Setiap pilihan yang Anda buat dalam mendesain alarm berulang dapat memiliki konsekuensi terhadap cara aplikasi menggunakan (atau menyalahgunakan) resource sistem. Misalnya, bayangkan suatu aplikasi populer yang disinkronkan dengan server. Jika operasi sinkronisasi didasarkan pada waktu jam dan setiap instance aplikasi disinkronkan pada pukul 23.00, beban terhadap server dapat menyebabkan latensi yang tinggi atau bahkan "denial of service". Ikuti praktik terbaik penggunaan alarm berikut:
- Tambahkan pengacakan (jitter) untuk semua permintaan jaringan
yang terpicu akibat alarm berulang:
- Lakukan semua pekerjaan lokal saat alarm terpicu. "Pekerjaan lokal" berarti semua pekerjaan yang tidak terhubung ke server atau memerlukan data dari server.
- Pada saat yang bersamaan, jadwalkan alarm yang berisi permintaan jaringan agar dipicu pada jangka waktu yang acak.
- Pertahankan agar frekuensi alarm Anda tetap minimum.
- Jangan aktifkan perangkat jika tidak perlu (perilaku ini ditentukan oleh jenis alarm, sebagaimana yang dijelaskan di Memilih jenis alarm).
- Jangan membuat waktu pemicu alarm yang lebih tepat dari yang diperlukan.
Gunakan
setInexactRepeating()
, dan bukansetRepeating()
. Saat Anda menggunakansetInexactRepeating()
, Android menyinkronkan alarm berulang dari beberapa aplikasi dan memicunya di saat yang bersamaan. Tindakan ini akan mengurangi jumlah total waktu yang digunakan sistem untuk mengaktifkan perangkat, sehingga mengurangi pemborosan baterai. Untuk Android versi 4.4 (API Level 19), semua alarm berulang tidak pasti. Perlu diperhatikan bahwa meskipunsetInexactRepeating()
merupakan peningkatan darisetRepeating()
, tetapi kode tersebut masih dapat membebani server jika setiap instance aplikasi menghubungkan ke server di saat yang hampir bersamaan. Oleh karena itu, untuk permintaan jaringan, tambahkan beberapa pengacakan ke alarm Anda, seperti yang dijelaskan di atas. - Jika memungkinkan, hindari mendasarkan alarm pada waktu jam.
Alarm berulang yang didasarkan pada waktu pemicu yang tepat tidak akan diskalakan dengan baik. Jika bisa, gunakan
ELAPSED_REALTIME
. Berbagai jenis alarm dijelaskan secara lebih mendetail di bagian berikut.
Menyetel alarm berulang
Seperti yang dijelaskan di atas, alarm berulang merupakan pilihan yang baik untuk menjadwalkan peristiwa rutin atau pencarian data. Alarm berulang memiliki karakteristik berikut:
- Jenis alarm. Untuk pembahasan lebih lanjut, buka Memilih jenis alarm.
- Waktu pemicu. Jika waktu pemicu yang Anda tentukan adalah di masa lalu, alarm akan langsung terpicu.
- Interval alarm. Misalnya, sehari sekali, setiap jam, setiap 5 menit, dan sebagainya.
- Intent tertunda yang terpicu saat alarm dipicu. Saat Anda menyetel alarm kedua yang menggunakan intent tertunda yang sama, alarm tersebut akan menggantikan alarm yang asli.
Untuk membatalkan PendingIntent
, teruskan
FLAG_NO_CREATE
ke PendingIntent.getService()
untuk mendapatkan instance intent (jika ada), lalu teruskan intent tersebut ke
AlarmManager.cancel()
:
Kotlin
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager val pendingIntent = PendingIntent.getService(context, requestId, intent, PendingIntent.FLAG_NO_CREATE) if (pendingIntent != null && alarmManager != null) { alarmManager.cancel(pendingIntent) }
Java
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pendingIntent = PendingIntent.getService(context, requestId, intent, PendingIntent.FLAG_NO_CREATE); if (pendingIntent != null && alarmManager != null) { alarmManager.cancel(pendingIntent); }
Memilih jenis alarm
Salah satu pertimbangan awal dalam menggunakan alarm berulang adalah jenisnya.
Ada dua jenis jam umum untuk alarm: "elapsed real time" dan "real time clock" (RTC). Elapsed real time menggunakan "time since system boot" sebagai referensi, dan real time clock menggunakan waktu UTC (jam dinding). Artinya, elapsed real time cocok untuk menyetel alarm berdasarkan waktu yang berlalu (misalnya, alarm yang terpicu setiap 30 detik) karena jenis alarm ini tidak terpengaruh oleh zona waktu/lokal. Jenis real time clock lebih cocok untuk alarm yang bergantung pada lokal saat ini.
Kedua jenis tersebut memiliki versi "wakeup", yang memberi tahu agar CPU perangkat diaktifkan jika layar nonaktif. Ini akan memastikan bahwa alarm dipicu pada waktu yang dijadwalkan. Ini bermanfaat jika aplikasi Anda memiliki dependensi waktu—misalnya, jika aplikasi memiliki jendela yang terbatas untuk melakukan operasi tertentu. Jika tidak menggunakan versi wakeup dari jenis alarm Anda, maka semua alarm berulang akan terpicu saat perangkat aktif di waktu berikutnya.
Jika Anda hanya memerlukan agar alarm terpicu pada interval tertentu, (misalnya setiap setengah jam), gunakan salah satu jenis elapsed real time. Secara umum, ini adalah pilihan yang lebih baik.
Jika Anda perlu agar alarm terpicu pada waktu tertentu dalam sehari, maka pilih salah satu jenis real time clock yang berbasis jam. Namun, perlu diperhatikan bahwa pendekatan ini dapat memiliki beberapa kekurangan—aplikasi mungkin tidak diterjemahkan dengan baik ke lokal lain, dan jika pengguna mengubah setelan waktu perangkat, tindakan tersebut dapat menyebabkan perilaku yang tidak diharapkan di aplikasi. Penggunaan jenis alarm real time clock juga tidak diskalakan dengan baik, seperti yang dijelaskan di atas. Jika memungkinkan, sebaiknya gunakan alarm "elapsed real time".
Berikut daftar jenis alarm:
ELAPSED_REALTIME
—Memicu intent tertunda berdasarkan berdasarkan waktu yang berlalu sejak perangkat di-booting, tetapi tidak membangunkan perangkat. Waktu berlalu termasuk kapan saja saat perangkat sedang tidur.ELAPSED_REALTIME_WAKEUP
—Mengaktifkan perangkat dan memicu intent tertunda setelah jangka waktu yang ditentukan telah berlalu sejak perangkat di-booting.RTC
—Memicu intent tertunda pada waktu yang ditentukan, tetapi tidak mengaktifkan perangkat.RTC_WAKEUP
—Mengaktifkan perangkat untuk memicu intent tertunda pada waktu yang ditentukan.
Contoh alarm elapsed real time
Berikut beberapa contoh penggunaan ELAPSED_REALTIME_WAKEUP
.
Mengaktifkan perangkat untuk memicu alarm dalam 30 menit, dan setiap 30 menit setelahnya:
Kotlin
// Hopefully your alarm will have a lower frequency than this! alarmMgr?.setInexactRepeating( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent )
Java
// Hopefully your alarm will have a lower frequency than this! alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
Mengaktifkan perangkat untuk memicu alarm satu kali (tidak berulang) dalam satu menit:
Kotlin
private var alarmMgr: AlarmManager? = null private lateinit var alarmIntent: PendingIntent ... alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent -> PendingIntent.getBroadcast(context, 0, intent, 0) } alarmMgr?.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000, alarmIntent )
Java
private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000, alarmIntent);
Contoh alarm real time clock
Berikut adalah beberapa contoh penggunaan RTC_WAKEUP
.
Mengaktifkan perangkat untuk memicu alarm kira-kira pada pukul 14.00, kemudian mengulanginya sekali sehari di waktu yang sama:
Kotlin
// Set the alarm to start at approximately 2:00 p.m. val calendar: Calendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() set(Calendar.HOUR_OF_DAY, 14) } // With setInexactRepeating(), you have to use one of the AlarmManager interval // constants--in this case, AlarmManager.INTERVAL_DAY. alarmMgr?.setInexactRepeating( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, AlarmManager.INTERVAL_DAY, alarmIntent )
Java
// Set the alarm to start at approximately 2:00 p.m. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 14); // With setInexactRepeating(), you have to use one of the AlarmManager interval // constants--in this case, AlarmManager.INTERVAL_DAY. alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent);
Mengaktifkan perangkat untuk memicu alarm tepat pada pukul 08.30, dan setiap 20 menit setelahnya:
Kotlin
private var alarmMgr: AlarmManager? = null private lateinit var alarmIntent: PendingIntent ... alarmMgr = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager alarmIntent = Intent(context, AlarmReceiver::class.java).let { intent -> PendingIntent.getBroadcast(context, 0, intent, 0) } // Set the alarm to start at 8:30 a.m. val calendar: Calendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() set(Calendar.HOUR_OF_DAY, 8) set(Calendar.MINUTE, 30) } // setRepeating() lets you specify a precise custom interval--in this case, // 20 minutes. alarmMgr?.setRepeating( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, 1000 * 60 * 20, alarmIntent )
Java
private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); // Set the alarm to start at 8:30 a.m. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 8); calendar.set(Calendar.MINUTE, 30); // setRepeating() lets you specify a precise custom interval--in this case, // 20 minutes. alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 20, alarmIntent);
Menentukan seberapa tepat alarm Anda seharusnya
Seperti yang dijelaskan di atas, memilih jenis alarm sering kali merupakan langkah pertama dalam pembuatan alarm.
Perbedaan lebih lanjut adalah seberapa tepat alarm Anda seharusnya. Untuk sebagian besar aplikasi,
setInexactRepeating()
adalah pilihan
yang tepat.
Saat Anda menggunakan metode ini, Android menyinkronkan beberapa alarm berulang tidak pasti dan memicunya
di waktu yang bersamaan. Ini akan mengurangi pemborosan baterai.
Untuk aplikasi langka yang memiliki persyaratan waktu yang kaku—misalnya, alarm harus
dipicu tepat pada pukul 08.30, dan setiap satu jam
setelahnya—gunakan setRepeating()
. Namun,
jika memungkinkan, sebaiknya hindari penggunaan alarm yang tepat.
Dengan setInexactRepeating()
,
Anda tidak dapat menentukan interval khusus seperti yang dapat Anda lakukan dengan
setRepeating()
. Anda harus menggunakan salah satu
konstanta interval, seperti INTERVAL_FIFTEEN_MINUTES
,
INTERVAL_DAY
, dan sebagainya. Buka AlarmManager
untuk melihat daftar lengkapnya.
Membatalkan alarm
Bergantung pada aplikasi, Anda mungkin ingin menyertakan kemampuan membatalkan alarm.
Untuk membatalkan alarm, panggil cancel()
di Alarm
Manager, yang meneruskan PendingIntent
yang tidak ingin Anda picu. Contoh:
Kotlin
// If the alarm has been set, cancel it. alarmMgr?.cancel(alarmIntent)
Java
// If the alarm has been set, cancel it. if (alarmMgr!= null) { alarmMgr.cancel(alarmIntent); }
Memulai alarm saat perangkat dimulai ulang
Secara default, semua alarm dibatalkan setelah perangkat nonaktif.
Untuk mencegah agar hal ini tidak terjadi, Anda dapat mendesain aplikasi
agar secara otomatis memulai ulang alarm berulang jika pengguna melakukan reboot pada perangkat. Tindakan ini memastikan
bahwa AlarmManager
akan terus melakukan tugasnya agar pengguna
tidak perlu memulai ulang alarm secara manual.
Berikut langkah-langkahnya:
- Tetapkan izin
RECEIVE_BOOT_COMPLETED
dalam manifes aplikasi Anda. Ini akan memungkinkan aplikasi Anda menerimaACTION_BOOT_COMPLETED
yang disiarkan setelah sistem menyelesaikan booting (ini hanya berfungsi jika aplikasi telah diluncurkan oleh pengguna minimal satu kali):<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
- Implementasikan
BroadcastReceiver
untuk menerima siaran:Kotlin
class SampleBootReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (intent.action == "android.intent.action.BOOT_COMPLETED") { // Set the alarm here. } } }
Java
public class SampleBootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { // Set the alarm here. } } }
- Tambahkan penerima ke file manifes aplikasi Anda dengan filter intent yang memfilter di
tindakan
ACTION_BOOT_COMPLETED
:<receiver android:name=".SampleBootReceiver" android:enabled="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"></action> </intent-filter> </receiver>
Perhatikan bahwa di manifes, penerima booting disetel ke
android:enabled="false"
. Artinya, penerima tidak akan dipanggil kecuali aplikasi secara eksplisit mengaktifkannya. Ini akan mencegah agar penerima booting tidak dipanggil jika tidak diperlukan. Anda dapat mengaktifkan penerima (misalnya, jika pengguna menyetel alarm) seperti berikut:Kotlin
val receiver = ComponentName(context, SampleBootReceiver::class.java) context.packageManager.setComponentEnabledSetting( receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP )
Java
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
Setelah Anda mengaktifkan penerima dengan cara ini, penerima akan terus diaktifkan, meskipun pengguna melakukan reboot pada perangkat. Dengan kata lain, pengaktifan penerima melalui program akan menggantikan setelan manifes, bahkan saat perangkat di-reboot. Penerima akan tetap aktif sampai aplikasi Anda menonaktifkannya. Anda dapat menonaktifkan penerima (misalnya, jika pengguna membatalkan alarm) seperti berikut:
Kotlin
val receiver = ComponentName(context, SampleBootReceiver::class.java) context.packageManager.setComponentEnabledSetting( receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP )
Java
ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
Dampak fitur Istirahatkan dan Aplikasi Standby
Fitur Istirahatkan dan Aplikasi Standby diperkenalkan di Android versi 6.0 (API level 23) sebagai upaya untuk memperpanjang
masa pakai baterai. Saat perangkat berada dalam mode Istirahatkan, semua alarm standar akan ditunda sampai
perangkat keluar dari mode Istirahatkan atau sampai masa pemeliharaan terbuka. Jika ada alarm yang harus terpicu bahkan dalam mode
Istirahatkan, Anda dapat menggunakan
setAndAllowWhileIdle()
atau
setExactAndAllowWhileIdle()
.
Aplikasi Anda akan memasuki mode Aplikasi Standby saat menganggur, yang berarti bahwa pengguna tidak menggunakannya selama
jangka waktu tertentu dan aplikasi tidak memiliki proses latar depan. Saat aplikasi berada dalam mode Aplikasi Standby, alarm
ditunda seperti dalam mode Istirahatkan. Batasan ini
dicabut saat aplikasi tidak lagi menganggur atau jika perangkat dicolokkan ke suplai daya. Untuk informasi
selengkapnya terkait dampak mode ini terhadap aplikasi Anda, baca
Mengoptimalkan Aplikasi untuk Mode Istirahatkan dan Aplikasi Standby.