Ringkasan siaran

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:

  1. 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.

  2. Sediakan subclass BroadcastReceiver dan implementasikan onReceive(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:

  1. Buat instance BroadcastReceiver.

    Kotlin

        val br: BroadcastReceiver = MyBroadcastReceiver()
        

    Java

        BroadcastReceiver br = new MyBroadcastReceiver();
        

  2. Buat IntentFilter dan daftarkan penerima dengan memanggil registerReceiver(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.

  3. 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 di onDestroy() untuk mencegah kebocoran penerima keluar dari konteks aktivitas. Jika mendaftarkan penerima di onResume(), Anda harus membatalkan pendaftaran di onPause() 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 di onSaveInstanceState(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 dalam manifesnya (lalu diberi izin jika berbahaya) yang dapat menerima siaran tersebut. Misalnya, kode berikut mengirimkan siaran:

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 setelah onReceive() kembali. Untuk informasi selengkapnya, lihat Efek pada status proses Untuk menjalankan tugas yang berjalan lama, sebaiknya:

    • Memanggil goAsync() dalam metode onReceive() penerima Anda dan meneruskan BroadcastReceiver.PendingResult ke thread latar belakang. Tindakan ini akan membuat siaran tetap aktif setelah kembali dari onReceive(). 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.
  • 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.