Aplikasi Android dapat mengirim atau menerima pesan siaran dari sistem Android dan aplikasi Android lainnya, mirip dengan pola desain publish-subscribe. Siaran ini dikirim saat peristiwa menarik terjadi. Misalnya, sistem Android mengirimkan siaran saat terjadi berbagai peristiwa sistem, seperti ketika sistem melakukan booting atau perangkat memulai pengisian daya. Aplikasi juga dapat mengirimkan siaran kustom, misalnya, untuk memberi tahu aplikasi lain tentang sesuatu yang mungkin menarik baginya (misalnya, beberapa data baru berhasil didownload).
Aplikasi dapat mendaftar untuk menerima siaran tertentu. Setelah siaran dikirim, sistem akan otomatis mengarahkan siaran ke aplikasi yang sudah berlangganan untuk menerima jenis siaran tertentu tersebut.
Pada umumnya, siaran dapat digunakan sebagai sistem messaging di seluruh aplikasi dan di luar alur penggunaan normal. Namun, Anda harus berhati-hati agar tidak menyalahgunakan kesempatan untuk merespons siaran dan menjalankan tugas di latar belakang yang dapat menyebabkan performa sistem lambat, seperti yang dijelaskan di video berikut.
Tentang siaran sistem
Sistem otomatis mengirim siaran ketika berbagai peristiwa sistem terjadi, seperti saat sistem beralih masuk dan keluar dari mode pesawat. Siaran sistem dikirim ke semua aplikasi yang berlangganan untuk menerima peristiwa tersebut.
Pesan siaran itu sendiri dikemas dalam objek Intent
yang string tindakannya mengidentifikasi peristiwa yang terjadi (misalnya android.intent.action.AIRPLANE_MODE
). Intent mungkin juga menyertakan informasi tambahan yang digabungkan ke dalam kolom tambahannya. Misalnya, intent mode pesawat menyertakan tambahan boolean yang menunjukkan aktif tidaknya Mode Pesawat.
Untuk informasi selengkapnya tentang cara membaca intent dan mendapatkan string tindakan dari intent, lihat Intent dan Filter Intent.
Untuk daftar lengkap tindakan siaran sistem, lihat file BROADCAST_ACTIONS.TXT
di Android SDK. Setiap tindakan siaran memiliki kolom konstanta yang terkait dengannya. Misalnya, nilai konstanta ACTION_AIRPLANE_MODE_CHANGED
adalah android.intent.action.AIRPLANE_MODE
. Dokumentasi untuk setiap tindakan siaran tersedia di kolom konstanta terkaitnya.
Perubahan pada siaran sistem
Seiring berkembangnya platform Android, platform ini secara berkala mengubah cara siaran sistem berperilaku. Ingat perubahan berikut jika aplikasi Anda menargetkan Android 7.0 (API level 24) atau yang lebih baru, atau jika aplikasi diinstal di perangkat yang menjalankan Android 7.0 atau yang lebih baru.
Android 9
Mulai Android 9 (API level 28), siaran NETWORK_STATE_CHANGED_ACTION
tidak menerima informasi tentang lokasi pengguna atau data identitas pribadi.
Selain itu, jika aplikasi diinstal di perangkat yang menjalankan Android 9 atau yang lebih baru, siaran sistem dari Wi-Fi tidak akan berisi SSID, BSSID, informasi koneksi, atau hasil pemindaian. Untuk mendapatkan informasi tersebut, panggil getConnectionInfo()
.
Android 8.0
Mulai Android 8.0 (API level 26), sistem menerapkan pembatasan tambahan pada penerima yang dinyatakan manifes.
Jika aplikasi Anda menargetkan Android 8.0 atau yang lebih baru, Anda tidak dapat menggunakan manifes guna menyatakan penerima untuk sebagian besar siaran implisit (siaran yang tidak menargetkan aplikasi Anda secara khusus). Anda masih bisa menggunakan penerima yang terdaftar dalam konteks jika pengguna menggunakan aplikasi Anda secara aktif.
Android 7.0
Android 7.0 (API level 24) dan yang lebih baru tidak mengirimkan siaran sistem berikut:
Selain itu, aplikasi yang menargetkan Android 7.0 dan yang lebih baru harus mendaftarkan siaran CONNECTIVITY_ACTION
menggunakan registerReceiver(BroadcastReceiver, IntentFilter)
.
Menyatakan penerima dalam manifes tidak akan berfungsi.
Menerima siaran
Aplikasi dapat menerima siaran dengan dua cara: melalui penerima yang dinyatakan manifes dan penerima yang terdaftar dalam konteks.
Penerima yang dinyatakan manifes
Jika Anda menyatakan penerima siaran dalam manifes, sistem akan meluncurkan aplikasi Anda (jika aplikasi belum berjalan) saat siaran dikirim.
Untuk menyatakan penerima siaran dalam manifes, lakukan langkah-langkah berikut:
Tentukan elemen
<receiver>
di manifes aplikasi Anda.<receiver android:name=".MyBroadcastReceiver" android:exported="true"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.INPUT_METHOD_CHANGED" /> </intent-filter> </receiver>
Filter intent akan menentukan tindakan siaran yang menjadi langganan penerima Anda.
Sediakan subclass
BroadcastReceiver
dan implementasikanonReceive(Context, Intent)
. Penerima siaran dalam contoh berikut mencatat dan menampilkan konten siaran:Kotlin
private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { StringBuilder().apply { append("Action: ${intent.action}\n") append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n") toString().also { log -> Log.d(TAG, log) Toast.makeText(context, log, Toast.LENGTH_LONG).show() } } } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); String log = sb.toString(); Log.d(TAG, log); Toast.makeText(context, log, Toast.LENGTH_LONG).show(); } }
Pengelola paket sistem akan mendaftarkan penerima setelah aplikasi terinstal. Penerima nantinya menjadi titik masuk terpisah ke aplikasi Anda, artinya sistem tersebut dapat memulai aplikasi dan mengirimkan siaran jika aplikasi tidak sedang berjalan.
Sistem membuat objek komponen BroadcastReceiver
baru untuk menangani setiap siaran yang diterimanya. Objek ini hanya berlaku selama durasi panggilan ke onReceive(Context, Intent)
. Setelah kode Anda kembali dari metode ini, sistem menganggap komponen tersebut tidak lagi aktif.
Penerima yang terdaftar dalam konteks
Untuk mendaftarkan penerima dalam konteks, lakukan langkah-langkah berikut:
Buat instance
BroadcastReceiver
.Kotlin
val br: BroadcastReceiver = MyBroadcastReceiver()
Java
BroadcastReceiver br = new MyBroadcastReceiver();
Buat
IntentFilter
dan daftarkan penerima dengan memanggilregisterReceiver(BroadcastReceiver, IntentFilter)
:Kotlin
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply { addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED) } registerReceiver(br, filter)
Java
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); this.registerReceiver(br, filter);
Penerima yang terdaftar dalam konteks akan menerima siaran selama konteks pendaftarannya valid. Misalnya, jika mendaftar dalam konteks
Activity
, Anda akan menerima siaran selama aktivitas tidak diakhiri. Jika mendaftar dengan konteks Aplikasi, Anda akan menerima siaran selama aplikasi berjalan.Untuk berhenti menerima siaran, panggil
unregisterReceiver(android.content.BroadcastReceiver)
. Pastikan untuk membatalkan pendaftaran penerima saat Anda tidak lagi membutuhkannya atau konteks tidak lagi valid.Perhatikan tempat Anda mendaftar dan membatalkan pendaftaran penerima. Misalnya, jika Anda mendaftarkan penerima di
onCreate(Bundle)
menggunakan konteks aktivitas, sebaiknya batalkan pendaftaran dionDestroy()
untuk mencegah kebocoran penerima keluar dari konteks aktivitas. Jika mendaftarkan penerima dionResume()
, Anda harus membatalkan pendaftaran dionPause()
untuk mencegah mendaftarkannya beberapa kali (Jika tidak ingin menerima siaran ketika dijeda, dan tindakan ini dapat menghindari overhead sistem yang tidak perlu). Jangan membatalkan pendaftaran dionSaveInstanceState(Bundle)
, karena ini tidak akan dipanggil jika pengguna kembali ke tumpukan histori.
Efek pada status proses
Status BroadcastReceiver
Anda (baik itu sedang berjalan atau tidak) memengaruhi status proses di dalamnya, yang pada akhirnya akan memengaruhi kemungkinan untuk dinonaktifkan oleh sistem. Misalnya, jika suatu proses menjalankan penerima (yang saat ini sedang menjalankan kode dalam metode onReceive()
-nya), proses tersebut dianggap sebagai proses latar depan. Sistem akan terus menjalankan proses tersebut kecuali dalam kasus tekanan memori ekstrem.
Namun, setelah kode kembali dari onReceive()
, BroadcastReceiver tidak lagi aktif. Proses host penerima menjadi sama pentingnya dengan komponen aplikasi lain yang berjalan di dalamnya. Jika proses tersebut hanya menghosting penerima yang dinyatakan manifes (kasus umum untuk aplikasi yang belum pernah berinteraksi dengan pengguna atau belum baru-baru ini), lalu setelah kembali dari onReceive()
, sistem akan menganggap prosesnya berprioritas rendah dan mungkin menonaktifkannya untuk menyediakan resource bagi proses lain yang lebih penting.
Untuk alasan tersebut, sebaiknya Anda tidak memulai thread latar belakang yang berjalan lama dari penerima siaran. Setelah onReceive()
, sistem dapat menonaktifkan proses kapan saja untuk mendapatkan kembali memori, dan dengan demikian, sistem akan menghentikan thread yang sedang berjalan dalam proses tersebut. Untuk menghindari hal ini, Anda harus memanggil goAsync()
(jika ingin sedikit lebih banyak waktu untuk memproses siaran dalam thread latar belakang) atau menjadwalkan JobService
dari penerima menggunakan JobScheduler
, sehingga sistem mengetahui bahwa proses tersebut terus menjalankan tugas aktif. Untuk informasi selengkapnya, lihat Proses dan Siklus Proses Aplikasi .
Cuplikan berikut menunjukkan BroadcastReceiver
yang menggunakan goAsync()
untuk menandai bahwa dibutuhkan lebih banyak waktu untuk menyelesaikannya setelah onReceive()
selesai. Hal ini sangat berguna jika tugas yang ingin Anda selesaikan dalam onReceive()
cukup lama yang akan menyebabkan UI thread kehilangan frame (>16 md), sehingga akan lebih cocok untuk thread latar belakang.
Kotlin
private const val TAG = "MyBroadcastReceiver" class MyBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val pendingResult: PendingResult = goAsync() val asyncTask = Task(pendingResult, intent) asyncTask.execute() } private class Task( private val pendingResult: PendingResult, private val intent: Intent ) : AsyncTask<String, Int, String>() { override fun doInBackground(vararg params: String?): String { val sb = StringBuilder() sb.append("Action: ${intent.action}\n") sb.append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n") return toString().also { log -> Log.d(TAG, log) } } override fun onPostExecute(result: String?) { super.onPostExecute(result) // Must call finish() so the BroadcastReceiver can be recycled. pendingResult.finish() } } }
Java
public class MyBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "MyBroadcastReceiver"; @Override public void onReceive(Context context, Intent intent) { final PendingResult pendingResult = goAsync(); Task asyncTask = new Task(pendingResult, intent); asyncTask.execute(); } private static class Task extends AsyncTask<String, Integer, String> { private final PendingResult pendingResult; private final Intent intent; private Task(PendingResult pendingResult, Intent intent) { this.pendingResult = pendingResult; this.intent = intent; } @Override protected String doInBackground(String... strings) { StringBuilder sb = new StringBuilder(); sb.append("Action: " + intent.getAction() + "\n"); sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n"); String log = sb.toString(); Log.d(TAG, log); return log; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); // Must call finish() so the BroadcastReceiver can be recycled. pendingResult.finish(); } } }
Mengirimkan siaran
Android memberikan tiga cara bagi aplikasi untuk mengirim siaran:
- Metode
sendOrderedBroadcast(Intent, String)
mengirim siaran ke satu penerima dalam satu waktu. Karena dijalankan bergantian, setiap penerima dapat menyebarkan hasilnya ke penerima berikutnya, atau dapat sepenuhnya membatalkan siaran sehingga tidak akan diteruskan ke penerima lain. Urutan penerima yang dijalankan dapat dikontrol dengan atribut android:priority pada filter intent yang cocok; penerima dengan prioritas yang sama akan dijalankan dalam urutan acak. - Metode
sendBroadcast(Intent)
mengirim siaran ke semua penerima dengan urutan yang tidak ditentukan. Ini disebut Siaran Normal. Siaran ini lebih efisien, tetapi penerima tidak dapat membaca hasil dari penerima lain, menyebarkan data yang diterima dari siaran, atau membatalkan siaran. - Metode
LocalBroadcastManager.sendBroadcast
mengirim siaran ke penerima yang berada di aplikasi yang sama dengan pengirim. Jika Anda tidak perlu mengirim siaran ke seluruh aplikasi, gunakan siaran lokal. Implementasinya akan jauh lebih efisien (tidak memerlukan komunikasi antar-proses) dan Anda tidak perlu khawatir tentang masalah keamanan apa pun yang terkait dengan aplikasi lain yang dapat menerima atau mengirim siaran Anda.
Cuplikan kode berikut menunjukkan cara mengirim siaran dengan membuat Intent dan memanggil sendBroadcast(Intent)
.
Kotlin
Intent().also { intent -> intent.setAction("com.example.broadcast.MY_NOTIFICATION") intent.putExtra("data", "Notice me senpai!") sendBroadcast(intent) }
Java
Intent intent = new Intent(); intent.setAction("com.example.broadcast.MY_NOTIFICATION"); intent.putExtra("data","Notice me senpai!"); sendBroadcast(intent);
Pesan siaran dikemas dalam objek Intent
.
String tindakan intent harus memberikan sintaks nama paket Java aplikasi dan mengidentifikasi peristiwa siaran secara unik. Anda dapat melampirkan informasi tambahan ke intent dengan putExtra(String, Bundle)
.
Anda juga dapat membatasi siaran ke sekumpulan aplikasi dalam organisasi yang sama dengan memanggil setPackage(String)
di intent.
Membatasi siaran dengan izin
Izin memungkinkan Anda untuk membatasi siaran ke sekumpulan aplikasi yang menyimpan izin tertentu. Anda dapat menerapkan pembatasan pada pengirim atau penerima siaran.
Mengirim dengan izin
Saat memanggil sendBroadcast(Intent, String)
atau sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
, Anda dapat menentukan parameter izin. Hanya penerima yang meminta izin dengan tag
Kotlin
sendBroadcast(Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS)
Java
sendBroadcast(new Intent("com.example.NOTIFY"), Manifest.permission.SEND_SMS);
Untuk menerima siaran, aplikasi penerima harus meminta izin seperti yang ditampilkan di bawah ini:
<uses-permission android:name="android.permission.SEND_SMS"/>
Anda dapat menentukan izin sistem yang ada seperti SEND_SMS
atau menetapkan izin kustom dengan elemen <permission>
. Untuk informasi tentang izin dan keamanan secara umum, lihat Izin Sistem.
Menerima dengan izin
Jika Anda menentukan parameter izin saat mendaftarkan penerima siaran (baik dengan registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
atau di tag <receiver>
dalam manifes Anda), maka hanya penyiar yang telah meminta izin dengan tag <uses-permission>
dalam manifesnya (dan diberi izin jika berbahaya) yang dapat mengirim Intent ke penerima.
Misalnya, anggap aplikasi penerima Anda memiliki penerima yang dinyatakan manifes seperti yang ditampilkan di bawah ini:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.SEND_SMS">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>
Atau aplikasi penerima Anda memiliki penerima yang terdaftar dalam konteks seperti yang ditunjukkan di bawah ini:
Kotlin
var filter = IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED) registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null )
Java
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
Kemudian, agar dapat mengirim siaran ke penerima tersebut, aplikasi yang mengirim harus meminta izin seperti yang ditampilkan di bawah ini:
<uses-permission android:name="android.permission.SEND_SMS"/>
Pertimbangan keamanan dan praktik terbaik
Berikut adalah beberapa pertimbangan keamanan dan praktik terbaik untuk mengirim dan menerima siaran:
Jika Anda tidak perlu mengirimkan siaran ke komponen di luar aplikasi, maka kirim dan terima siaran lokal dengan
LocalBroadcastManager
yang tersedia di Support Library.LocalBroadcastManager
jauh lebih efisien (tidak memerlukan komunikasi antarproses) dan memungkinkan Anda menghindari masalah keamanan yang terkait dengan aplikasi lain yang dapat menerima atau mengirim siaran. Siaran Lokal dapat digunakan sebagai eventbus pub/sub untuk tujuan umum di aplikasi Anda tanpa overhead siaran seluruh sistem.Jika banyak aplikasi yang telah terdaftar untuk menerima siaran yang sama dalam manifesnya, hal ini dapat membuat sistem meluncurkan banyak aplikasi, menyebabkan dampak yang signifikan pada performa perangkat dan pengalaman pengguna. Untuk menghindari hal tersebut, gunakan pendaftaran konteks daripada pernyataan manifes. Terkadang, sistem Android sendiri menerapkan penggunaan penerima yang terdaftar dalam konteks. Misalnya, siaran
CONNECTIVITY_ACTION
hanya dikirimkan ke penerima yang terdaftar dalam konteks.Jangan menyiarkan informasi sensitif menggunakan intent implisit. Informasi tersebut dapat dibaca oleh semua aplikasi yang didaftarkan untuk menerima siaran. Ada tiga cara untuk mengontrol siapa saja yang dapat menerima siaran Anda:
- Anda dapat menentukan izin saat mengirimkan siaran.
- Di Android 4.0 dan yang lebih baru, Anda dapat menentukan paket dengan
setPackage(String)
saat mengirim siaran. Sistem membatasi siaran ke sekumpulan aplikasi yang cocok dengan paket tersebut. - Anda dapat mengirim siaran lokal dengan
LocalBroadcastManager
.
Setelah mendaftarkan penerima, semua aplikasi dapat mengirimkan siaran yang berpotensi berbahaya ke penerima aplikasi Anda. Ada tiga cara untuk membatasi siaran yang diterima aplikasi Anda:
- Anda dapat menentukan izin saat mendaftarkan penerima siaran.
- Untuk penerima yang dinyatakan manifes, Anda dapat menetapkan atribut android:exported ke "false" di manifes. Penerima tersebut tidak akan menerima siaran dari sumber di luar aplikasi.
- Anda dapat membatasi ke siaran lokal saja dengan
LocalBroadcastManager
.
Ruang nama untuk tindakan siaran bersifat global. Pastikan nama tindakan dan string lain ditulis dalam ruang nama yang Anda miliki, atau jika tidak, mungkin secara tidak sengaja akan bertentangan dengan aplikasi lain.
Karena berjalan di thread utama, metode
onReceive(Context, Intent)
penerima seharusnya dapat berjalan dan kembali dengan cepat. Jika perlu menjalankan tugas yang berjalan lama, berhati-hatilah saat menghasilkan thread atau memulai layanan latar belakang karena sistem dapat mengakhiri seluruh proses setelahonReceive()
kembali. Untuk informasi selengkapnya, lihat Efek pada status proses Untuk menjalankan tugas yang berjalan lama, sebaiknya:- Memanggil
goAsync()
dalam metodeonReceive()
penerima Anda dan meneruskanBroadcastReceiver.PendingResult
ke thread latar belakang. Tindakan ini akan membuat siaran tetap aktif setelah kembali darionReceive()
. Namun, meski dengan pendekatan ini, sistem mengharapkan Anda menyelesaikan siaran dengan sangat cepat (kurang dari 10 detik). Hal ini memungkinkan Anda memindahkan tugas ke thread lain untuk menghindari gangguan pada thread utama. - Menjadwalkan tugas dengan
JobScheduler
. Untuk informasi selengkapnya, lihat Penjadwalan Tugas Cerdas.
- Memanggil
Jangan memulai aktivitas dari penerima siaran karena pengalaman pengguna akan terasa janggal; terutama jika ada lebih dari satu penerima. Sebagai gantinya, pertimbangkan untuk menampilkan notifikasi.