目次
ブロードキャストは良いことです. 私たちのプログラムは、ブロードキャストを送受信して、他のプログラムにニュースを通知したり、他のプログラムからメッセージを受信したりできます.
1.放送の仕組み
ブロードキャストには次の 2 種類があります。
-
標準放送
これは非同期ブロードキャストです。ブロードキャストが送信された後、対応するすべてのブロードキャスト レシーバーはブロードキャスト メッセージを受信できます。このようなブロードキャストはより効率的ですが、一度送信すると傍受できません。
-
整然とした放送
名前が示すように、この種のブロードキャストは順次送信される同期ブロードキャストです。重みがブロードキャストされる順序に従います。また、ブロードキャストを最初に受信したプログラムがブロードキャストを切り捨て、後続の受信者が受信できなくなる可能性があります。
ブロードキャストを受け入れるには、次の 2 つの方法があります。
-
動的登録
このようにして、プログラム内でいつでもブロードキャストレシーバーを登録または登録解除できます。たとえば、Activity の onResume() でブロードキャスト レシーバを登録し、onPause() でブロードキャスト レシーバをキャンセルします。これの利点は、非常に柔軟であることですが、制限は、Activity を受け取る前に開始する必要があることです。
-
静的登録
BroadcastReceiver を継承するクラスを作成し、このブロードキャスト レシーバを AndroidManifest.xml に登録し、受け入れるブロードキャスト タイプを指定します。
2.放送を探索する
1.動的登録
まず動的登録を使用して、システムからブロードキャストを受信してみましょう。
新しいアクティビティを作成し、内部クラスを作成します。
inner class TimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "the time has changed!", Toast.LENGTH_SHORT).show()
}
}
このクラスは、ブロードキャスト レシーバである BroadcastReceiver を継承します。次に onReceive() メソッドを書き直し、ブロードキャストを受信したときに処理するロジックを処理します。
次に、onCreate() でブロードキャスト レシーバーを動的に登録します。
lateinit var timeChangeReceiver: TimeChangeReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dynamic_broadcast)
timeChangeReceiver = TimeChangeReceiver()
val intentFilter = IntentFilter()
// 系统自带的时间改变的广播
intentFilter.addAction("android.intent.action.TIME_TICK")
registerReceiver(timeChangeReceiver, intentFilter)
}
ここで registerReceiver() が呼び出され、ブロードキャスト レシーバーが登録されます。最初のパラメータはブロードキャスト レシーバを指定するためのもので、2 番目のパラメータは受信するブロードキャストを指定するために使用される IntentFilter です。android.intent.action.TIME_TICK は、Android システムに付属するブロードキャストです。このブロードキャストは、時間 (分単位) が変わると送信されます。
ブロードキャストは、登録後に登録解除する必要があることに注意してください
// 记得要注销掉接收器
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(timeChangeReceiver)
}
プログラムを実行すると、毎分プロンプトが表示されます。
2. 静的登録と標準ブロードキャストの送信
前の動的登録と同様に、Activity が開始されない場合、ブロードキャスト レシーバーを登録することはできません。しかし、静的登録にはそのような制限はありません。
ここで、ブロードキャスト レシーバー BroadcastReceiver を作成できます。
-
Exported は、この番組以外の放送を受け入れることが許可されているかどうかを示します。
-
Enabled は、Receiver が開始されているかどうかを示します。
作成後は、onReceiv() メソッドを再起動するだけです。ただし、Androidmanifest.xml では、Android Studio は既にこのレシーバーを登録しています。
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
</receiver>
次に、この Receiver が受信するブロードキャスト メソッドを指定します。これは、Adroidmanifest.xml でも定義されています。
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="com.bingbin.MY_BROADCAST" />
</intent-filter>
</receiver>
ここでは、com.bingbin.MY_BROADCAST ブロードキャストを受信することを定義し、ブロードキャストの送信を実装します。
新しいアクティビティを作成し、それにボタンを追加してブロードキャストを送信します。
btn_sendBroadcast.setOnClickListener {
val intent = Intent("com.bingbin.MY_BROADCAST")
intent.setPackage(packageName)
sendBroadcast(intent)
}
ブロードキャストを送信するメソッドは、sendBoradcast() であり、Intent() を渡す必要があります。これを作成したときに、ブロードキャストが com.bingbin.MY_BROADCAST で送信されるように指定しました。
インテント.setPackage(packageName) という行がある理由を次に示します。暗黙のブロードキャストとは、電源オン ブロードキャストや時間変更ブロードキャストなど、送信プログラムを指定しないブロードキャストを指します。静的に登録されたレシーバーはこれらのブロードキャストを受信できますが、これにより一部のプログラムが悪意を持ってブロードキャストを受信してリソースを消費する原因となるため、Android では静的に登録されたレシーバーが暗黙的なブロードキャストを受信することを禁止しています。したがって、ここでは、このブロードキャストを受信するために、同じパッケージ名でプログラムを指定する必要があります。
ボタンをクリックすると、プロンプト メッセージが表示されます。
標準ブロードキャストを確認するために、ここで別の BroadcastReceiver を作成して、com.bingbin.MY_BROADCAST ブロードキャストも受信します。
class AnotherReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "another receiver also has received my broadcast",
Toast.LENGTH_SHORT).show()
}
}
<receiver
android:name=".AnotherReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="com.bingbin.MY_BROADCAST" />
</intent-filter>
</receiver>
このとき、もう一度ボタンを押すと、2 つのプロンプト メッセージが表示されます。
3. 順番にブロードキャストを送信する
標準ブロードキャストを送信するメソッドは sendBroadcast() で、順序付きブロードキャストを送信するメソッドは sendOrderBoradcast() です。
順序付けられたブロードキャストを送信する別のボタンを追加しましょう。
btn_sendOrderBroadcast.setOnClickListener {
val intent = Intent("com.bingbin.MY_BROADCAST")
intent.setPackage(packageName)
sendOrderedBroadcast(intent, null)
}
2 番目のパラメーターは null に設定できます。
順送放送なので、順番はどうやって決めればいいですか?重みは、Receiver によって AndroidManifest.xml に登録された <intent-filter> タグで判断できます。
<receiver
android:name=".AnotherReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="10">
<action android:name="com.bingbin.MY_BROADCAST" />
</intent-filter>
</receiver>
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="100">
<action android:name="com.bingbin.MY_BROADCAST" />
</intent-filter>
</receiver>
このように、MyBroadcastReceiver が最初にブロードキャストを受信し、AnotherReceiver が後でブロードキャストを受信します。
MyBroadcastReceiver がブロードキャストを受信するロジックで abortBroadcast() メソッドを呼び出すと、ブロードキャストが切断され、AnotherReceiver がブロードキャストを受信できなくなります。
このように、MyBroadcastReceiver のメッセージだけがポップアップします。
3. 質問する
最初の質問
以前使用していたシステムの android.intent.action.TIME_TICK ブロードキャストは標準ブロードキャストですか、それとも順序付きブロードキャストですか? 動的に登録された BroadcastReceiver の優先度を設定するには?
2番目の質問
BroadcastReceiver は複数のブロードキャストを受信できますか? そうでない場合、指定された 2 つの異なるブロードキャストが一定期間内に受信された場合にのみロジックがトリガーされるという要件がある場合、どのように実装する必要がありますか?
3番目の質問
静的に登録された受信者のライフ サイクルはどのようなものですか? 複数のブロードキャストが連続して受信される場合、それは新しいレシーバーですか、それとも毎回同じインスタンスですか?
4. 疑問を探る
最初の質問
まず、android.intent.action.TIME_TICK は暗黙のブロードキャストであるため、静的登録メソッドを使用してテストすることはできず、動的登録 BroadcastReceiver のみを使用できます。
次に、1 つのクラスに 2 つの内部 BroadcastReceiver を記述し、それらに優先順位を設定します。優先順位の高い BroadcastReceiver はブロードキャストをインターセプトして、優先順位の低い方がまだ受信できるかどうかを確認します。
ここで、ちなみに、動的に登録された BroadcastReceiver の優先度をどのように設定するかという問題が解決されます。ブロードキャストを登録するときに IntentFilter() パラメータを渡す必要があり、優先度は IntentFilter で設定できます。
class DynamicBroadcastActivity : AppCompatActivity() {
lateinit var timeChangeReceiver: TimeChangeReceiver
lateinit var anotherReceiver : AnotherTimeChangeReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dynamic_broadcast)
timeChangeReceiver = TimeChangeReceiver()
val filter1 = IntentFilter()
// 系统自带的时间改变的广播
filter1.addAction("android.intent.action.TIME_TICK")
filter1.priority = 100
registerReceiver(timeChangeReceiver, filter1)
anotherReceiver = AnotherTimeChangeReceiver()
val filter2 = IntentFilter()
// 系统自带的时间改变的广播
filter2.addAction("android.intent.action.TIME_TICK")
filter2.priority = 10
registerReceiver(anotherReceiver, filter2)
}
// 记得要注销掉接收器
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(timeChangeReceiver)
unregisterReceiver(anotherReceiver)
}
inner class TimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "Time has changed!", Toast.LENGTH_SHORT).show()
// 尝试拦截
abortBroadcast()
}
}
inner class AnotherTimeChangeReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "AnotherTimeChangeReceiver has received the broadcast",
Toast.LENGTH_SHORT).show()
}
}
}
テストの結果、AnotherTimeChangeReceiver がまだブロードキャストを受信していることがわかりました。このシステム放送が標準放送であることを示している。
おそらく、システムのブロードキャストが規則的なブロードキャストである場合、悪意のあるプログラムは、それを傍受するために高い優先度で BroadcastReceiver を登録し、問題が発生する可能性があります。
2番目の質問
これをテストするために、2 つのブロードキャストを送信するボタンを定義してみましょう。
btn_sendTwoBroadcast.setOnClickListener {
val broad1 = Intent("com.bingbin.BROADCAST1")
broad1.setPackage(packageName)
val broad2 = Intent("com.bingbin.BROADCAST2")
broad2.setPackage(packageName)
sendBroadcast(broad1)
sendBroadcast(broad2)
}
次に、静的に登録された Receiver で 2 つのブロードキャストの受信を試みます。
<receiver
android:name=".StrongReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.bingbin.BROADCAST1"/>
<action android:name="com.bingbin.BROADCAST2"/>
</intent-filter>
</receiver>
次に、処理ロジックを追加します
class StrongReceiver : BroadcastReceiver() {
var i = 0
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "I have received a broadcast $i", Toast.LENGTH_SHORT).show()
i += 1
}
}
何回呼び出されたかを区別するために、グローバル変数を追加しました。
ログを確認しました
出力が2回出力されていることがわかりましたが、私は変わりませんでした。これは少し奇妙です。
後で i を静的クラスに入れました
出力が正しいことがわかります。
したがって、BroadcastReceiver が複数のブロードキャストを受信するように指定できることがここで判断できます。
3番目の質問
実際、2 番目の質問の実践から、Receiver がブロードキャストを受信するたびに、それは新しいインスタンスになるはずであると推測できます。
もう一度確認しよう
class MyBroadcastReceiver : BroadcastReceiver() {
init {
Log.d("MyBroadcastReceiver", "创建了一个新的实例")
}
override fun onReceive(context: Context, intent: Intent) {
Toast.makeText(context, "I have received my own broadcast!",
Toast.LENGTH_SHORT).show()
}
}
ここでは、MyBroadcastReceiver のコンストラクターにログ出力の一文を追加しました。
ブロードキャストが受信されるたびに、静的に登録されたレシーバーが新しいインスタンスを作成することがわかります。
次に、以前に作成されたインスタンスが常にメモリ内に存在するかどうかという問題が発生します。いつ破壊されますか?破棄しないと OOM になりますか?
するとonReceive()のソースコードにこんなコメントを発見
この BroadcastReceiver が <receiver> タグを介して起動された場合、この関数から戻った後、オブジェクトはもはや生きていません。これは、非同期的に結果を返す操作を実行してはならないことを意味します。
したがって、静的に登録されたレシーバーの場合、onReceive() を呼び出した後に破棄されることがわかります。メモリのリスクはありません。
動的に登録する場合、 unregisterReceiver() をアクティブに呼び出してレシーバーをキャンセルすることにも注意を払う必要があります。そうしないと、メモリ リークのリスクが生じる可能性があります。