メモリリークや不要なリソース消費を避ける、Broadcast Receiverのライフサイクル管理
はじめに
Android アプリでは、システムや他のアプリからの情報を受け取るために Broadcast Receiver が使われます。しかし、Broadcast Receiver のライフサイクルを適切に管理しないと、メモリリークや不要なリソース消費につながる可能性があります。
この記事では、Broadcast Receiver の基本とandroidx.lifecycle
ライブラリを用いたライフサイクル管理について、コードスニペットを交えながら解説します。
Broadcast Receiver とは
Broadcast Receiver は、Android システムやアプリ間で送受信されるブロードキャストメッセージを受け取るためのコンポーネントです。
主に次の 2 種類のブロードキャストがあります。
システムブロードキャスト
例: android.intent.action.BOOT_COMPLETED
(デバイス起動完了時の通知)
カスタムブロードキャスト
例: アプリ内で定義した独自のメッセージ
Broadcast Receiver の登録方法
Broadcast Receiver は 2 つの方法で登録できます。次のセクションで、順に見ていきましょう。
- AndroidManifest.xml で静的登録
- コードで動的登録
1. 静的登録
アプリ起動中でなくてもブロードキャストを受け取ることができます。
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
2. 動的登録
アクティビティやサービスが実行中の場合にのみブロードキャストを受け取ります。
val myReceiver = MyBroadcastReceiver()
val intentFilter = IntentFilter("com.example.CUSTOM_ACTION")
registerReceiver(myReceiver, intentFilter)
動的登録を使用する場合、ライフサイクルに応じた適切な登録と解除が必要です。これを怠ると、メモリリークやアプリのクラッシュにつながります。次のセクションで、少し深ぼって解説します。
ライフサイクル管理
Broadcast Receiver の登録解除を忘れると、メモリリークが発生します。動的登録の場合、特に意識すべき点があります。
良い例と望ましくない例を比較しながら見ていきましょう。
androidx.lifecycle
を活用したライフサイクル管理
良い例: 後述するミスは、登録解除を手動で行うことで引き起こされます。ReceiverLifecycleObserver
を活用し、自動化しましょう。
class MyBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// ブロードキャストを受信したときの処理
if (intent.action == "com.example.CUSTOM_ACTION") {
Log.d("MyBroadcastReceiver", "Custom action received")
}
}
}
class ReceiverLifecycleObserver(
private val receiver: BroadcastReceiver,
private val filter: IntentFilter
) : DefaultLifecycleObserver {
override fun onStart(owner: LifecycleOwner) {
owner.context.registerReceiver(receiver, filter)
}
override fun onStop(owner: LifecycleOwner) {
owner.context.unregisterReceiver(receiver)
}
}
class MainActivity : AppCompatActivity() {
private lateinit var myReceiver: MyBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myReceiver = MyBroadcastReceiver()
val intentFilter = IntentFilter("com.example.CUSTOM_ACTION")
lifecycle.addObserver(ReceiverLifecycleObserver(myReceiver, intentFilter))
}
}
よくあるミス:メモリリークや不要なリソース消費につながるコード
onDestroy
で `Receiver を解除し忘れる
1: class MainActivity : AppCompatActivity() {
private lateinit var myReceiver: MyBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Broadcast Receiverを動的に登録
myReceiver = MyBroadcastReceiver()
val intentFilter = IntentFilter("com.example.CUSTOM_ACTION")
registerReceiver(myReceiver, intentFilter)
}
// unregisterReceiverが呼ばれない
}
2: 同じ Receiver を複数回登録
class MainActivity : AppCompatActivity() {
private lateinit var myReceiver: MyBroadcastReceiver
override fun onStart() {
super.onStart()
// 毎回新しいReceiverを登録してしまう
myReceiver = MyBroadcastReceiver()
val intentFilter = IntentFilter("com.example.CUSTOM_ACTION")
registerReceiver(myReceiver, intentFilter)
}
override fun onStop() {
super.onStop()
// 最後の1つしか解除しないため、以前のReceiverが残る
unregisterReceiver(myReceiver)
}
}
良い例
onDestroy
で Receiver を自動的に解除
1: class MainActivity : AppCompatActivity() {
private lateinit var myReceiver: MyBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
myReceiver = MyBroadcastReceiver()
val intentFilter = IntentFilter("com.example.CUSTOM_ACTION")
// ReceiverLifecycleObserverを登録
lifecycle.addObserver(ReceiverLifecycleObserver(this, myReceiver, intentFilter))
}
}
2: 複数のアクションを 1 つの Receiver で管理
複数のアクションを個別の Receiver で管理するのではなく、1 つの Receiver でまとめて処理します。
class UnifiedBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
"com.example.ACTION_ONE" -> Log.d("UnifiedReceiver", "Action One received")
"com.example.ACTION_TWO" -> Log.d("UnifiedReceiver", "Action Two received")
}
}
}
class MainActivity : AppCompatActivity() {
private lateinit var unifiedReceiver: UnifiedBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
unifiedReceiver = UnifiedBroadcastReceiver()
val intentFilter = IntentFilter().apply {
addAction("com.example.ACTION_ONE")
addAction("com.example.ACTION_TWO")
}
lifecycle.addObserver(ReceiverLifecycleObserver(this, unifiedReceiver, intentFilter))
}
}
おわりに
Broadcast Receiver を使用する際には、ライフサイクルを意識して適切に登録・解除を行うことが重要です。特に、例外処理や状態管理を行うことで、予期しない動作やメモリリークを防ぎます。androidx.lifecycle
ライブラリを適切に活用し、この処理を自動化すると、そのような心配は無用になります。
公式の実装を参考に、安全かつ効率的な Broadcast Receiver の活用を目指しましょう。
浅学のため、この記事には粗があるかと思います。お気づきの点があれば、ぜひコメントや SNS で教えていただけると幸いです! 🙏✨
Discussion