ブロードキャストの概要

Android アプリは、publish-subscribe 設計パターンと同様に、Android システムや他の Android アプリからブロードキャスト メッセージを送受信できます。これらのブロードキャストは、対象のイベントが発生したときに送信されます。たとえば、Android システムは、システムの起動時やデバイスの充電開始時など、さまざまなシステム イベントが発生したときにブロードキャストを送信します。アプリはカスタム ブロードキャストを送信することもできます。たとえば、アプリが興味を持ちそうな内容(新しいデータがダウンロードされたなど)を他のアプリに通知できます。

システムは、最適なシステム状態を維持するためにブロードキャストの配信を最適化します。そのため、ブロードキャストの配信時間は保証されません。低レイテンシのプロセス間通信を必要とするアプリは、バインドされたサービスを検討する必要があります。

アプリは特定のブロードキャストを受信するように登録できます。ブロードキャストが送信されると、特定のタイプのブロードキャストを受信するようにサブスクライブしているアプリに、システムが自動的にブロードキャストをルーティングします。

一般的に、ブロードキャストはアプリ間や通常のユーザーフローの外部でメッセージ システムとして使用できます。ただし、ブロードキャストに応答し、バックグラウンドでジョブを実行する機会を悪用しないように注意してください。これは、システム パフォーマンスの低下につながる可能性があります。

システム ブロードキャストについて

機内モードのオンとオフが切り替わるときなど、さまざまなシステム イベントが発生すると、システムが自動的にブロードキャストを送信します。システム ブロードキャストは、イベントを受信するように登録されているすべてのアプリに送信されます。

ブロードキャスト メッセージ自体は、発生したイベントを識別するアクション文字列(android.intent.action.AIRPLANE_MODE など)を含む Intent オブジェクトにラップされます。インテントには、追加フィールドにバンドルされた追加情報を含めることもできます。たとえば、機内モードのインテントには、機内モードがオンになっているかどうかを示すブール値のエクストラが含まれています。

インテントを読み取り、インテントからアクション文字列を取得する方法について詳しくは、インテントとインテント フィルタをご覧ください。

システム ブロードキャスト アクションの完全なリストについては、Android SDK の BROADCAST_ACTIONS.TXT ファイルをご覧ください。各ブロードキャスト アクションには、関連付けられた定数フィールドがあります。たとえば、定数 ACTION_AIRPLANE_MODE_CHANGED の値は android.intent.action.AIRPLANE_MODE です。各ブロードキャスト アクションのドキュメントは、関連する定数フィールドで確認できます。

システム ブロードキャストの変更

Android プラットフォームの進化に伴い、システム ブロードキャストの動作は定期的に変更されています。Android のすべてのバージョンをサポートするには、次の変更に注意してください。

Android 14

アプリがキャッシュされた状態にある間、ブロードキャスト配信はシステムの健全性に合わせて最適化されます。たとえば、アプリがキャッシュに保存された状態にある場合、ACTION_SCREEN_ON などの重要性の低いシステム ブロードキャストは延期されます。アプリがキャッシュに保存された状態からアクティブなプロセスのライフサイクルに移行すると、システムは遅延ブロードキャストを配信します。

マニフェストで宣言された重要なブロードキャストにより、配信のためにキャッシュされた状態からアプリが一時的に削除されます。

Android 9

Android 9(API レベル 28)以降、NETWORK_STATE_CHANGED_ACTION ブロードキャストは、ユーザーの位置情報や個人を特定できるデータを受信しません。

また、Android 9 以降を搭載したデバイスにアプリがインストールされている場合、Wi-Fi からのシステム ブロードキャストには SSID、BSSID、接続情報、スキャン結果は含まれません。この情報を取得するには、getConnectionInfo() を呼び出します。

Android 8.0

Android 8.0(API レベル 26)以降では、マニフェストで宣言されたレシーバに追加の制限が適用されます。

Android 8.0 以降をターゲットとするアプリの場合、ほとんどの非明示的ブロードキャスト(特定のアプリをターゲットにしていないブロードキャスト)に対して、マニフェストを使用してレシーバを宣言することはできません。ユーザーがアプリを積極的に使用している場合は、コンテキスト登録されたレシーバを引き続き使用できます。

Android 7.0

Android 7.0(API レベル 24)以降では、次のシステム ブロードキャストは送信されません。

また、Android 7.0 以降をターゲットとするアプリでは、registerReceiver(BroadcastReceiver, IntentFilter) を使用して CONNECTIVITY_ACTION ブロードキャストを登録する必要があります。マニフェストでレシーバーを宣言することはできません。

ブロードキャストの受信

アプリは、マニフェスト宣言されたレシーバとコンテキスト登録されたレシーバの 2 つの方法でブロードキャストを受信できます。

マニフェストで宣言されたレシーバー

マニフェストでブロードキャスト レシーバを宣言している場合は、ブロードキャストの送信時にシステムがアプリを起動します(アプリがまだ実行されていない場合)。

マニフェストでブロードキャストのレシーバーを宣言する手順は、次のとおりです。

  1. アプリのマニフェストで <receiver> 要素を指定します。

    <!-- If this receiver listens for broadcasts sent from the system or from
         other apps, even other apps that you own, set android:exported to "true". -->
    <receiver android:name=".MyBroadcastReceiver" android:exported="false">
        <intent-filter>
            <action android:name="APP_SPECIFIC_BROADCAST" />
        </intent-filter>
    </receiver>
    

    インテント フィルタで、レシーバーが登録するブロードキャスト アクションを指定します。

  2. サブクラス BroadcastReceiveronReceive(Context, Intent) を実装します。次の例のブロードキャスト レシーバは、ブロードキャストの内容をログに記録して表示します。

    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)
    
                    val binding = ActivityNameBinding.inflate(layoutInflater)
                    val view = binding.root
                    setContentView(view)
    
                    Snackbar.make(view, log, Snackbar.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);
    
                ActivityNameBinding binding =
                        ActivityNameBinding.inflate(layoutInflater);
                val view = binding.root;
                setContentView(view);
    
                Snackbar.make(view, log, Snackbar.LENGTH_LONG).show();
            }
        }
    

    ビュー バインディングを有効にするには、モジュール レベルの build.gradle ファイルで viewBinding を構成します。

アプリのインストール時に、システム パッケージ管理システムがレシーバーを登録します。これにより、レシーバはアプリへの個別のエントリ ポイントになります。つまり、アプリが現在実行されていない場合、システムはアプリを起動してブロードキャストを配信できます。

システムは、受信した各ブロードキャストを処理する新しい BroadcastReceiver コンポーネント オブジェクトを作成します。このオブジェクトは、onReceive(Context, Intent) の呼び出し中のみ有効です。このメソッドからコードが返されると、コンポーネントはもうアクティブでなくなったと見なします。

コンテキスト登録されたレシーバー

コンテキスト登録されたレシーバは、登録コンテキストが有効である限りブロードキャストを受信します。たとえば、Activity コンテキスト内で登録すると、アクティビティが破棄されない限り、ブロードキャストを受信します。アプリ コンテキストに登録すると、アプリが実行されている限り、ブロードキャストを受信します。

コンテキストを使用してレシーバーを登録する手順は、次のとおりです。

  1. アプリのモジュール レベルのビルドファイルに、AndroidX Core ライブラリのバージョン 1.9.0 以降を組み込みます。

    Groovy

    dependencies {
        def core_version = "1.12.0"
    
        // Java language implementation
        implementation "androidx.core:core:$core_version"
        // Kotlin
        implementation "androidx.core:core-ktx:$core_version"
    
        // To use RoleManagerCompat
        implementation "androidx.core:core-role:1.0.0"
    
        // To use the Animator APIs
        implementation "androidx.core:core-animation:1.0.0-rc01"
        // To test the Animator APIs
        androidTestImplementation "androidx.core:core-animation-testing:1.0.0-rc01"
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation "androidx.core:core-performance:1.0.0"
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation "androidx.core:core-google-shortcuts:1.1.0"
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation "androidx.core:core-remoteviews:1.1.0-beta01"
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation "androidx.core:core-splashscreen:1.1.0-rc01"
    }
    

    Kotlin

    dependencies {
        val core_version = "1.12.0"
    
        // Java language implementation
        implementation("androidx.core:core:$core_version")
        // Kotlin
        implementation("androidx.core:core-ktx:$core_version")
    
        // To use RoleManagerCompat
        implementation("androidx.core:core-role:1.0.0")
    
        // To use the Animator APIs
        implementation("androidx.core:core-animation:1.0.0-rc01")
        // To test the Animator APIs
        androidTestImplementation("androidx.core:core-animation-testing:1.0.0-rc01")
    
        // Optional - To enable APIs that query the performance characteristics of GMS devices.
        implementation("androidx.core:core-performance:1.0.0")
    
        // Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Google
        implementation("androidx.core:core-google-shortcuts:1.1.0")
    
        // Optional - to support backwards compatibility of RemoteViews
        implementation("androidx.core:core-remoteviews:1.1.0-beta01")
    
        // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12
        implementation("androidx.core:core-splashscreen:1.1.0-rc01")
    }
    
  2. BroadcastReceiver のインスタンスを作成します。

    Kotlin

    val br: BroadcastReceiver = MyBroadcastReceiver()
    

    Java

    BroadcastReceiver br = new MyBroadcastReceiver();
    
  3. IntentFilter のインスタンスを作成します。

    Kotlin

    val filter = IntentFilter(APP_SPECIFIC_BROADCAST)
    

    Java

    IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);
    
  4. ブロードキャスト レシーバをエクスポートして、デバイス上の他のアプリに表示するかどうかを選択します。このレシーバがシステムから送信されたブロードキャストや他のアプリ(自分が所有している他のアプリも含む)から送信されたブロードキャストをリッスンする場合は、RECEIVER_EXPORTED フラグを使用します。このレシーバがアプリから送信されたブロードキャストのみをリッスンする場合は、RECEIVER_NOT_EXPORTED フラグを使用します。

    Kotlin

    val listenToBroadcastsFromOtherApps = false
    val receiverFlags = if (listenToBroadcastsFromOtherApps) {
        ContextCompat.RECEIVER_EXPORTED
    } else {
        ContextCompat.RECEIVER_NOT_EXPORTED
    }
    

    Java

    boolean listenToBroadcastsFromOtherApps = false;
    if (listenToBroadcastsFromOtherApps) {
        receiverFlags = ContextCompat.RECEIVER_EXPORTED;
    } else {
        receiverFlags = ContextCompat.RECEIVER_NOT_EXPORTED;
    }
    
  5. registerReceiver() を呼び出してレシーバを登録します。

    Kotlin

    ContextCompat.registerReceiver(context, br, filter, receiverFlags)
    

    Java

    ContextCompat.registerReceiver(context, br, filter, receiverFlags);
    
  6. ブロードキャストの受信を停止するには、unregisterReceiver(android.content.BroadcastReceiver) を呼び出します。レシーバーが不要になった場合、またはコンテキストが無効になった場合は、必ずレシーバーの登録を解除してください。

    レシーバの登録と登録解除を行う場所に注意してください。たとえば、アクティビティのコンテキストを使用して onCreate(Bundle) にレシーバを登録する場合は、onDestroy() でレシーバの登録を解除して、レシーバがアクティビティ コンテキストから漏洩しないようにする必要があります。onResume() でレシーバを登録する場合は、複数回登録されないように onPause() で登録を解除する必要があります(一時停止時にブロードキャストを受信したくない場合、不要なシステム オーバーヘッドを削減できます)。onSaveInstanceState(Bundle) では登録を解除しないでください。ユーザーが履歴スタック内に戻しても呼び出されないためです。

プロセス状態への影響

BroadcastReceiver が動作しているかどうかは、そのプロセスに影響するため、システム強制終了の可能性が変化する可能性があります。フォアグラウンド プロセスは、レシーバーの onReceive() メソッドを実行します。極端なメモリ負荷がかかった状態を除いて、システムはプロセスを実行します。

BroadcastReceiver は onReceive() 後に無効になります。レシーバのホストプロセスは、そのアプリ コンポーネントに左右されます。そのプロセスが、マニフェストで宣言されたレシーバーのみをホストする場合(ユーザーが最近操作していない、または最近使用していないアプリでよく見られます)、システムが onReceive() の後にそのレシーバーを強制終了し、他のより重要なプロセスでリソースを使用できるようにします。

したがって、ブロードキャスト レシーバは長時間実行バックグラウンド スレッドを開始しないでください。onReceive() の後はいつでもプロセスを停止してメモリを回収し、作成されたスレッドを終了できます。プロセスを維持するには、JobScheduler を使用してレシーバーからの JobService をスケジュールし、プロセスがまだ機能していることをシステムが認識できるようにします。詳しくは、バックグラウンド処理の概要をご覧ください。

ブロードキャストの送信

Android では、アプリがブロードキャストを送信する 3 つの方法が用意されています。

  • sendOrderedBroadcast(Intent, String) メソッドは、一度に 1 つのレシーバにブロードキャストを送信します。各レシーバは順番に実行されるため、結果を次のレシーバに伝播するか、ブロードキャストを完全に中止して他のレシーバに渡されないようにします。レシーバの実行順序は、一致するインテント フィルタの android:priority 属性で制御できます。同じ優先度のレシーバは任意の順序で実行されます。
  • sendBroadcast(Intent) メソッドは、未定義の順序ですべてのレシーバにブロードキャストを送信します。これは通常ブロードキャストと呼ばれます。これはより効率的ですが、レシーバは他のレシーバからの結果の読み取り、ブロードキャストから受信したデータの伝播、ブロードキャストの中止ができなくなります。

次のコード スニペットは、インテントを作成して sendBroadcast(Intent) を呼び出して、ブロードキャストを送信する方法を示しています。

Kotlin

Intent().also { intent ->
    intent.setAction("com.example.broadcast.MY_NOTIFICATION")
    intent.putExtra("data", "Nothing to see here, move along.")
    sendBroadcast(intent)
}

Java

Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data", "Nothing to see here, move along.");
sendBroadcast(intent);

ブロードキャスト メッセージは Intent オブジェクトにラップされます。インテントのアクション文字列では、アプリの Java パッケージ名の構文を指定し、ブロードキャスト イベントを一意に識別する必要があります。putExtra(String, Bundle) を使用してインテントに追加情報を追加できます。インテントで setPackage(String) を呼び出すことで、同じ組織内の一連のアプリにブロードキャストを制限することもできます。

権限の設定によるブロードキャストの制限

権限を使用すると、特定の権限を保持するアプリセットにブロードキャストを制限できます。ブロードキャストの送信者または受信者のいずれかに制限を適用できます。

権限を設定した送信

sendBroadcast(Intent, String) または sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle) を呼び出すときに、権限パラメータを指定できます。ブロードキャストを受信できるのは、マニフェストで タグを使用してその権限をリクエストし、その後、危険性がある場合は権限を付与したレシーバのみです。たとえば、次のコードはブロードキャストを送信します。

Kotlin

sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

Java

sendBroadcast(new Intent(BluetoothDevice.ACTION_FOUND),
              Manifest.permission.BLUETOOTH_CONNECT)

ブロードキャストを受信するには、受信側のアプリが下記のように権限をリクエストする必要があります。

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

BLUETOOTH_CONNECT などの既存のシステム権限を指定することも、<permission> 要素を使用してカスタム権限を定義することもできます。権限とセキュリティに関する一般的な情報については、システム権限をご覧ください。

権限を設定した受信

ブロードキャスト レシーバの登録時に(registerReceiver(BroadcastReceiver, IntentFilter, String, Handler) またはマニフェストの <receiver> タグで)権限パラメータを指定した場合、マニフェストで <uses-permission> タグを使用して権限をリクエストした(その後、危険な場合は権限を付与された)ブロードキャスターのみがレシーバにインテントを送信できます。

たとえば、受信側アプリに、次のようにマニフェストで宣言されたレシーバーがあるとします。

<receiver android:name=".MyBroadcastReceiver"
          android:permission="android.permission.BLUETOOTH_CONNECT">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_FOUND"/>
    </intent-filter>
</receiver>

または、受信アプリにコンテキスト登録されたレシーバーがあるとします。

Kotlin

var filter = IntentFilter(Intent.ACTION_FOUND)
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )

Java

IntentFilter filter = new IntentFilter(Intent.ACTION_FOUND);
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null );

その後、これらのレシーバにブロードキャストを送信できるようにするため、送信側アプリは、次のように権限をリクエストする必要があります。

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>

セキュリティに関する考慮事項とおすすめの方法

ブロードキャストの送受信に関するセキュリティ上の考慮事項とベスト プラクティスは次のとおりです。

  • 多くのアプリがマニフェストで同じブロードキャストを受信するよう登録している場合、システムが多数のアプリを起動し、デバイスのパフォーマンスとユーザー エクスペリエンスの両方に大きな影響を及ぼす可能性があります。これを回避するには、マニフェストの宣言よりもコンテキスト登録を使用します。Android システム自体が、コンテキスト登録されたレシーバの使用を強制する場合があります。たとえば、CONNECTIVITY_ACTION ブロードキャストはコンテキスト登録されたレシーバにのみ配信されます。

  • 暗黙的なインテントを使用して機密情報をブロードキャストしないでください。この情報は、ブロードキャストの受信登録を行うすべてのアプリで読み取ることができます。ブロードキャストを受信できるレシーバーを制御するには、次の 3 つの方法があります。

    • ブロードキャストの送信時に権限を指定できます。
    • Android 4.0 以降では、ブロードキャストの送信時に setPackage(String) を使用してパッケージを指定できます。ブロードキャストは、パッケージに一致するアプリセットに限定されます。
  • レシーバを登録すると、どのアプリからも、悪質な可能性のあるブロードキャストがアプリのレシーバに送信される可能性があります。アプリが受信するブロードキャストを制限する方法はいくつかあります。

    • ブロードキャストのレシーバーを登録する際に、権限を指定できます。
    • マニフェストで宣言されたレシーバーの場合は、マニフェスト内で android:exported 属性を「false」に設定できます。レシーバはアプリ外のソースからのブロードキャストを受信しません。
  • ブロードキャスト アクションの名前空間はグローバルです。アクション名やその他の文字列は、所有する名前空間に記述してください。そうしないと、誤って他のアプリと競合する可能性があります。

  • レシーバの onReceive(Context, Intent) メソッドはメインスレッドで実行されるため、迅速に実行して戻る必要があります。長時間実行される処理を実行する必要がある場合は、onReceive() が返されるとシステムがプロセス全体を強制終了する可能性があるため、スレッドの生成やバックグラウンド サービスの開始に注意してください。詳細については、プロセスの状態への影響をご覧ください。長時間実行処理を行うには、次のことをおすすめします。

    • レシーバの onReceive() メソッドで goAsync() を呼び出し、BroadcastReceiver.PendingResult をバックグラウンド スレッドに渡す。これにより、onReceive() から戻った後もブロードキャストがアクティブなままになります。ただし、このアプローチでも、システムはブロードキャストが迅速に(10 秒未満)完了することが期待されます。処理を別のスレッドに移動することで、メインスレッドの不具合を回避できます。
    • JobScheduler を使用して、ジョブのスケジュール設定を行います。詳細については、インテリジェントなジョブ スケジューリングをご覧ください。
  • 特に複数のレシーバが存在する場合、ユーザー エクスペリエンスが低下するため、ブロードキャスト レシーバからアクティビティを開始しないでください。代わりに、通知を表示することを検討してください。