Android アプリは、パブリッシュ / サブスクライブのデザイン パターンと同様に、Android システムや他の Android アプリからブロードキャスト メッセージを送受信します。通常、システムとアプリは特定のイベントが発生したときにブロードキャストを送信します。たとえば、システムの起動やデバイスの充電など、さまざまなシステム イベントが発生したときに、Android システムがブロードキャストを送信します。また、アプリからカスタム ブロードキャストが送信されることもあります。新しいデータのダウンロードなど他のアプリに関係する可能性のある情報の通知などが行われます。
アプリは特定のブロードキャストを受信するように登録できます。ブロードキャストが送信されると、特定の種類のブロードキャストを受信するように登録されたアプリにシステムから自動的にブロードキャストが送信されます。
ブロードキャストは、アプリ間のメッセージング システムとして使用されたり、通常のユーザーフローの外部のメッセージング システムとして使用されるのが一般的です。ただし、頻繁にブロードキャストに応答して、バックグラウンドでジョブを実行しすぎないように注意する必要があります。システム パフォーマンスの低下につながる可能性があります。
システム ブロードキャストについて
システムで機内モードのオンとオフが切り替えられるなど、さまざまなシステム イベントが発生するとシステムが自動的にブロードキャストを送信します。登録されているすべてのアプリがこれらのブロードキャストを受け取ります。
Intent
オブジェクトはブロードキャスト メッセージをラップします。action
文字列は、発生したイベントを識別します(例: android.intent.action.AIRPLANE_MODE
)。インテントに追加フィールドにバンドルされた追加情報も含めることができます。たとえば、機内モードのインテントには、機内モードがオンかどうかを示す追加のブール値が含まれます。
インテントを読み取って、インテントからアクション文字列を取得する方法について詳しくは、インテントとインテント フィルタをご覧ください。
システム ブロードキャスト アクション
システム ブロードキャストのすべてのアクションの一覧については、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.0(API レベル 28)以降を実行するデバイスにインストールされている場合、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 つの方法でブロードキャストを受信できます。
コンテキスト登録されたレシーバー
コンテキスト登録されたレシーバーは、登録コンテキストが有効である限りブロードキャストを受信します。通常、これは registerReceiver
と unregisterReceiver
の呼び出しの間に行われます。システムが対応するコンテキストを破棄すると、登録コンテキストも無効になります。たとえば、Activity
コンテキスト内で登録すると、アクティビティがアクティブである限りブロードキャストを受信します。アプリケーションのコンテキストに登録すると、アプリが実行されている限りブロードキャストを受信します。
コンテキストを使用してレシーバーを登録する手順は、次のとおりです。
アプリのモジュール レベルのビルドファイルに、AndroidX Core ライブラリのバージョン 1.9.0 以降を追加します。
Groovy
dependencies { def core_version = "1.13.1" // 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" // To test the Animator APIs androidTestImplementation "androidx.core:core-animation-testing:1.0.0" // 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" // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation "androidx.core:core-splashscreen:1.2.0-alpha02" }
Kotlin
dependencies { val core_version = "1.13.1" // 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") // To test the Animator APIs androidTestImplementation("androidx.core:core-animation-testing:1.0.0") // 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") // Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12 implementation("androidx.core:core-splashscreen:1.2.0-alpha02") }
BroadcastReceiver
のインスタンスを作成します。Kotlin
val myBroadcastReceiver = MyBroadcastReceiver()
Java
MyBroadcastReceiver myBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter
のインスタンスを作成します。Kotlin
val filter = IntentFilter("com.example.snippets.ACTION_UPDATE_DATA")
Java
IntentFilter filter = new IntentFilter("com.example.snippets.ACTION_UPDATE_DATA");
ブロードキャスト レシーバをエクスポートしてデバイス上の他のアプリから参照可能にするかどうかを選択します。このレシーバが、システムまたは他のアプリ(所有する他のアプリを含む)から送信されたブロードキャストをリッスンする場合は、
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; int receiverFlags = listenToBroadcastsFromOtherApps ? ContextCompat.RECEIVER_EXPORTED : ContextCompat.RECEIVER_NOT_EXPORTED;
registerReceiver()
を呼び出してレシーバーを登録します。Kotlin
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags)
Java
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, receiverFlags);
ブロードキャストの受信を停止するには、
unregisterReceiver(android.content.BroadcastReceiver)
を呼び出します。レシーバーが不要になった場合、またはコンテキストが有効でなくなった場合には、必ずレシーバーの登録を解除します。
ブロードキャスト レシーバの登録を解除する
ブロードキャスト レシーバが登録されている間、レシーバは登録したコンテキストへの参照を保持します。レシーバの登録スコープがコンテキストのライフサイクル スコープを超えると、リークが発生する可能性があります。たとえば、アクティビティ スコープにレシーバーを登録したときに、システムがアクティビティを破棄するときに登録を解除し忘れた場合に、この問題が発生する可能性があります。そのため、ブロードキャスト レシーバーは必ず登録解除してください。
Kotlin
class MyActivity : ComponentActivity() {
private val myBroadcastReceiver = MyBroadcastReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags)
setContent { MyApp() }
}
override fun onDestroy() {
super.onDestroy()
// When you forget to unregister your receiver here, you're causing a leak!
this.unregisterReceiver(myBroadcastReceiver)
}
}
Java
class MyActivity extends ComponentActivity {
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
ContextCompat.registerReceiver(this, myBroadcastReceiver, filter, receiverFlags);
// Set content
}
}
レシーバーを最小のスコープに登録する
ブロードキャスト レシーバは、結果に実際に関心がある場合にのみ登録する必要があります。可能な限り小さいレシーバ スコープを選択します。
LifecycleResumeEffect
またはアクティビティのonResume
/onPause
ライフサイクル メソッド: ブロードキャスト レシーバは、アプリが再開状態にある間のみ更新を受け取ります。LifecycleStartEffect
またはアクティビティのonStart
/onStop
ライフサイクル メソッド: ブロードキャスト レシーバは、アプリが再開状態にある間のみ更新を受け取ります。DisposableEffect
: ブロードキャスト レシーバは、コンポーザブルがコンポジション ツリー内にある間のみ更新を受信します。このスコープは、アクティビティのライフサイクル スコープに関連付けられていません。アプリケーション コンテキストでレシーバを登録することを検討してください。これは、コンポーザブルが理論上、アクティビティのライフサイクル スコープを超えてアクティビティをリークする可能性があるためです。- アクティビティ
onCreate
/onDestroy
: ブロードキャスト レシーバは、アクティビティが作成済み状態のときに更新を受信します。onSaveInstanceState(Bundle)
では呼び出されないため、onDestroy()
で登録解除してください。 - カスタム スコープ: たとえば、
ViewModel
スコープにレシーバを登録すると、アクティビティの再作成後もレシーバが存続します。レシーバーはアクティビティのライフサイクル スコープよりも長く存続し、アクティビティをリークする可能性があるため、必ずアプリケーション コンテキストを使用してレシーバーを登録してください。
ステートフルとステートレスのコンポーザブルを作成する
Compose には、ステートフルなコンポーザブルとステートレスなコンポーザブルがあります。コンポーザブル内でブロードキャスト レシーバを登録または登録解除すると、コンポーザブルはステートフルになります。コンポーザブルは、同じパラメータを渡されたときに同じコンテンツをレンダリングする確定的関数ではありません。内部状態は、登録されたブロードキャスト レシーバーの呼び出しに基づいて変更される場合があります。
Compose のベスト プラクティスとして、コンポーザブルをステートフル バージョンとステートレス バージョンに分割することをおすすめします。そのため、ブロードキャスト レシーバの作成を Composable からホイスティングして、ステートレスにすることをおすすめします。
@Composable
fun MyStatefulScreen() {
val myBroadcastReceiver = remember { MyBroadcastReceiver() }
val context = LocalContext.current
LifecycleStartEffect(true) {
// ...
ContextCompat.registerReceiver(context, myBroadcastReceiver, filter, flags)
onStopOrDispose { context.unregisterReceiver(myBroadcastReceiver) }
}
MyStatelessScreen()
}
@Composable
fun MyStatelessScreen() {
// Implement your screen
}
マニフェストで宣言されたレシーバー
マニフェストでブロードキャストのレシーバーを宣言すると、ブロードキャストが送信された際にシステムがアプリを起動します。アプリがまだ実行されていない場合、システムはアプリを起動します。
マニフェストでブロードキャストのレシーバーを宣言する手順は、次のとおりです。
アプリのマニフェストで
<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="com.example.snippets.ACTION_UPDATE_DATA" /> </intent-filter> </receiver>
インテント フィルタで、レシーバーが登録するブロードキャスト アクションを指定します。
サブクラス
BroadcastReceiver
でonReceive(Context, Intent)
を実装します。次の例では、ブロードキャスト レシーバーでブロードキャストの内容の記録と表示を行います。Kotlin
class MyBroadcastReceiver : BroadcastReceiver() { @Inject lateinit var dataRepository: DataRepository override fun onReceive(context: Context, intent: Intent) { if (intent.action == "com.example.snippets.ACTION_UPDATE_DATA") { val data = intent.getStringExtra("com.example.snippets.DATA") ?: "No data" // Do something with the data, for example send it to a data repository: dataRepository.updateData(data) } } }
Java
public static class MyBroadcastReceiver extends BroadcastReceiver { @Inject DataRepository dataRepository; @Override public void onReceive(Context context, Intent intent) { if (Objects.equals(intent.getAction(), "com.example.snippets.ACTION_UPDATE_DATA")) { String data = intent.getStringExtra("com.example.snippets.DATA"); // Do something with the data, for example send it to a data repository: if (data != null) { dataRepository.updateData(data); } } } }
システムのパッケージ マネージャーは、アプリのインストール時にレシーバーを登録します。その後、レシーバーはアプリの個別のエントリ ポイントになります。つまり、アプリが実行されていない場合でも、システムがアプリを起動してブロードキャストを配信できるということです。
システムは、受信した各ブロードキャストを処理するために新しい BroadcastReceiver
コンポーネント オブジェクトを作成します。このオブジェクトは、onReceive(Context, Intent)
の呼び出し中のみ有効です。このメソッドからコードが返されると、システムはコンポーネントがアクティブでないとみなします。
プロセス状態への影響
BroadcastReceiver
が動作しているかどうかは、含まれるプロセスに影響し、システムが強制終了される可能性を変更する可能性があります。フォアグラウンド プロセスは、レシーバーの onReceive()
メソッドを実行します。メモリに対して過度の負荷が発生している場合を除き、システムがプロセスを実行します。
システムは onReceive()
の後に BroadcastReceiver
を無効にします。レシーバーのホストプロセスの重要度は、アプリのコンポーネントによって異なります。そのプロセスがマニフェストで宣言されたレシーバーのみをホストしている場合、システムは onReceive()
後にそのプロセスを強制終了し、他のより重要なプロセス用にリソースを解放することがあります。これは、ユーザーが操作したことがないアプリや最近操作していないアプリでよく見られます。
したがって、ブロードキャスト レシーバーで長時間実行されるバックグラウンド スレッドを開始しないでください。システムは onReceive()
の終了後にいつでもプロセスを停止してメモリを再要求し、作成されたスレッドを終了できます。プロセスを維持するには、JobScheduler
を使用してレシーバーから JobService
のスケジュールを設定し、プロセスがまだ実行中であることをシステムに認識させます。詳しくは、バックグラウンド処理の概要をご覧ください。
ブロードキャストを送信する
Android では、アプリがブロードキャストを送信する 2 つの方法が用意されています。
sendOrderedBroadcast(Intent, String)
メソッドは一度に 1 つのレシーバーにブロードキャストを送信します。各レシーバーは順番に実行されるため、次のレシーバーに結果を伝播できます。ブロードキャストを完全に中止して、他のレシーバーに到達しないようにすることもできます。レシーバの実行順序を制御できます。これを行うには、一致するインテント フィルタの android:priority 属性を使用します。同じ優先度のレシーバーは任意の順序で実行されます。sendBroadcast(Intent)
メソッドは、すべてのレシーバーにブロードキャストを送信しますが、順序は定義されません。これは、ノーマル ブロードキャストと呼ばれます。これはより効率的ですが、レシーバーが他のレシーバーから結果を読み取ることはできません。また、ブロードキャストから受信したデータの伝達や、ブロードキャストの破棄もできません。
次のコード スニペットは、インテントを作成して sendBroadcast(Intent)
を呼び出し、ブロードキャストを送信する方法を示しています。
Kotlin
val intent = Intent("com.example.snippets.ACTION_UPDATE_DATA").apply {
putExtra("com.example.snippets.DATA", newData)
setPackage("com.example.snippets")
}
context.sendBroadcast(intent)
Java
Intent intent = new Intent("com.example.snippets.ACTION_UPDATE_DATA");
intent.putExtra("com.example.snippets.DATA", newData);
intent.setPackage("com.example.snippets");
context.sendBroadcast(intent);
ブロードキャスト メッセージは Intent
オブジェクトでラップされています。インテントの action
文字列では、構文に沿ってアプリの Java パッケージ名を指定し、ブロードキャスト イベントを一意に識別できるようにする必要があります。putExtra(String, Bundle)
を使用して、インテントに追加情報を追加できます。インテントで setPackage(String)
を呼び出すことにより、ブロードキャストを同じ組織内のアプリセットに制限することもできます。
権限の設定によるブロードキャストの制限
権限を設定して、ブロードキャストの対象を一定の権限を持つアプリセットに制限できます。ブロードキャストの送信側または受信側に対して制限を適用できます。
権限を設定したブロードキャストを送信する
sendBroadcast(Intent, String)
または sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String,
Bundle)
を呼び出す際に、権限パラメータを指定できます。マニフェストで <uses-permission>
タグを使用して権限をリクエストしたレシーバーのみが、ブロードキャストを受信できます。権限が危険な場合は、レシーバがブロードキャストを受信する前に権限を付与する必要があります。たとえば、次のコードは権限付きでブロードキャストを送信します。
Kotlin
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION)
Java
context.sendBroadcast(intent, android.Manifest.permission.ACCESS_COARSE_LOCATION);
ブロードキャストを受信するには、受信側のアプリが次のように権限をリクエストする必要があります。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
BLUETOOTH_CONNECT
などの既存のシステム権限を指定することも、<permission>
要素を使用してカスタム権限を定義することもできます。一般的な権限とセキュリティについて詳しくは、システム権限をご覧ください。
権限を設定したブロードキャストを受信する
ブロードキャスト レシーバーを登録する際に権限パラメータを指定した場合(registerReceiver(BroadcastReceiver, IntentFilter, String, Handler)
またはマニフェストの <receiver>
タグで指定した場合)、マニフェストの <uses-permission>
タグで権限をリクエストしたブロードキャスターのみが、レシーバーにインテントを送信できます。権限が危険な場合は、ブロードキャスターにも権限を付与する必要があります。
たとえば、以下に示すように、受信アプリにマニフェストで宣言されたレシーバーがあるとします。
<!-- 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=".MyBroadcastReceiverWithPermission"
android:permission="android.permission.ACCESS_COARSE_LOCATION"
android:exported="true">
<intent-filter>
<action android:name="com.example.snippets.ACTION_UPDATE_DATA" />
</intent-filter>
</receiver>
または、受信アプリにコンテキスト登録されたレシーバーがあるとします。
Kotlin
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
)
Java
ContextCompat.registerReceiver(
context, myBroadcastReceiver, filter,
android.Manifest.permission.ACCESS_COARSE_LOCATION,
null, // scheduler that defines thread, null means run on main thread
receiverFlags
);
次に、それらのレシーバーにブロードキャストを送信できるようにするには、以下のようにして、送信アプリから権限をリクエストする必要があります。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
セキュリティ上の考慮事項
ブロードキャストの送受信に関するセキュリティ上の考慮事項は次のとおりです。
マニフェストで多数のアプリが同じブロードキャストを受信するように登録されていると、システムにより多数のアプリが起動されることになり、デバイスのパフォーマンスとユーザー エクスペリエンスの両方に大きな影響を与えます。これを回避するには、マニフェストの宣言ではなくコンテキスト登録を使用します。Android システム自体が、コンテキスト登録されたレシーバーの使用を強制することもあります。たとえば、
CONNECTIVITY_ACTION
ブロードキャストはコンテキスト登録されたレシーバーにのみ配信されます。暗黙的なインテントを使用して機密情報をブロードキャストしないでください。ブロードキャストを受信するように登録されているアプリであれば、どのアプリでも情報を読み取ることができます。ブロードキャストを受信できるレシーバーを制御するには、次の 3 つの方法があります。
- ブロードキャストの送信時に権限を指定できます。
- Android 4.0(API レベル 14)以降では、ブロードキャストの送信時に
setPackage(String)
を使用してパッケージを指定できます。システムは、ブロードキャストをパッケージに一致するアプリセットに制限します。
レシーバーを登録すると、どのアプリからでも悪意のあるブロードキャストをアプリのレシーバーに送信できる恐れが生じます。アプリで受信するブロードキャストを制限するには、次の方法があります。
- ブロードキャストのレシーバーを登録する際に、権限を指定できます。
- マニフェストで宣言されたレシーバーの場合は、マニフェストで android:exported 属性を「false」に設定できます。レシーバーが、アプリの外部にあるソースからのブロードキャストを受信しなくなります。
ブロードキャスト アクションの名前空間はグローバルです。アクション名とその他の文字列は、所有する名前空間に記述するようにしてください。そうしないと、他のアプリと意図せず競合する可能性があります。
レシーバーの
onReceive(Context, Intent)
メソッドはメインスレッドで実行されるため、実行とリターンを迅速に行う必要があります。長時間に渡る処理を実行する必要がある場合は、onReceive()
が返された後にシステムがプロセス全体を強制終了する可能性があるため、スレッドの生成やバックグラウンド サービスの開始に注意してください。詳しくは、プロセスの状態への影響をご覧ください。長時間に渡る処理を実行する場合は、次のようにすることをおすすめします。- レシーバーの
onReceive()
メソッドでgoAsync()
を呼び出し、BroadcastReceiver.PendingResult
をバックグラウンド スレッドに渡します。そうすることで、onReceive()
から返された後も、ブロードキャストがアクティブな状態に保たれます。ただし、この方法でも、短時間(10 秒未満)でのブロードキャストの終了がシステムでは想定されているため、メインスレッドに不具合が発生しないように、別のスレッドに処理を移動することが許可されます。 JobScheduler
を使用して、ジョブのスケジュール設定を行います。詳細については、インテリジェントなジョブ スケジューリングをご覧ください。
- レシーバーの
ブロードキャスト レシーバーからアクティビティを開始しないようにしてください。特に複数のレシーバーが存在する場合に、ユーザー エクスペリエンスが低下します。代わりに、通知の表示を検討してください。