概述
广播是 Android
四大组件之一,是 Android
系统提供的一种通讯方式。Android
中的广播机制允许程序可以只对自己感兴趣的广播进行注册,使得程序只会接收到自己所关心的广播内容。
Android
中的广播有来自系统的,比如电量变化、wifi
连接变化开机完成等,还有来自其他应用程序的,如自定义广播等。
在 Android
系统中要接受广播就需要使用 BroadcastReceiver
。
广播的应用场景
- 不同组件间的通信(含: 应用内和不同应用之间)
- 多线程通信
- 组件和系统之间的通信(比如网路变化)
广播类型
- 标准广播:一种完全异步执行的广播,发出广播后,该广播事件的接收者,几乎会在同一时刻收到通知,都可以响应或不响应该事件
- 有序广播:一种同步执行的广播,发出广播后,同一时刻,只有一个广播接收者能收到、一个接收者处理完后之后,可以选择继续向下传递给其它接收者,也可以拦截掉广播。
注册方式
上面我们说过 Android
中的广播机制允许程序可以只对自己感兴趣的广播进行注册。这里的“注册”又可以分为两种:
- 动态注册: 利用代码的方式注册【推荐使用】
- 静态注册:在清单文件中注册,从
Android
8.0 开始,所有隐式广播都不允许使用静态注册的方式来接收了,建议大家不要在清单文件中静态注册广播接收者
示例 1:接收系统的广播
这里以系统广播中的关闭或打开飞行模式为例子,当我们关闭或打开飞行模式时,系统会发一条 Intent.ACTION_AIRPLANE_MODE_CHANGED
广播。广播的实现基本上就两个步骤:
- 实现
BroadcastReceiver
,重写onReceive()
函数 - 注册
BroadcastReceiver
实现 BroadcastReceiver
的代码如下:
class TestBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_AIRPLANE_MODE_CHANGED ->
Toast.makeText(context, "您的飞行模式状态改变了", Toast.LENGTH_SHORT).show()
}
}
}
注意:这里的
onReceive()
函数是在主线程中,不要处理耗时的操作。也不要开启一个子线程去执行耗时操作,因为会出现在子线程还没有结束的情况下,Activity
已经被用户退出了,或者BroadcastReceiver
已经结束了的情况。
注册 BroadcastReceiver
的代码如下:
class TestBroadcastReceiverActivity : AppCompatActivity() {
private lateinit var mReceiver: TestBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_broadcast_receiver)
mReceiver = TestBroadcastReceiver()
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
registerReceiver(mReceiver, intentFilter)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(mReceiver)
}
}
效果如下所示:
示例 2:发送自定义广播并接收数据
在发送广播之前,我们同样需要实现 BroadcastReceiver
,重写 onReceive()
函数,因为这样我们发出去的自定义广播才能被接收。代码如下所示:
class TestBroadcastReceiver(private val tv: TextView?) : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_AIRPLANE_MODE_CHANGED ->
Toast.makeText(context, "您的飞行模式状态改变了", Toast.LENGTH_SHORT).show()
"com.geely.interview.TEST_BROADCAST_RECEIVER" -> {
val data = intent.getStringExtra("content")
Toast.makeText(context, "接收到了自定义广播,携带的数据为$data", Toast.LENGTH_LONG).show()
tv?.let { it.text = data }
}
}
}
}
这里我在主构造函数中传入
TextView
,主要是为了一会将接收到的值设置到界面中,这里可以用接口,也可以将TestBroadcastReceiver
直接写在Activity
里面等,这里我为了简单起见,直接将TextView
从构造函数中传过来了。后面的示例 5,改成了利用接口来实现将值设置到界面中。
发送广播只需要构建一个 Intent
对象,并把要发送的广播的值传入,然后调用 sendBroadcast()
方法就可以将广播发送出去了。具体代码如下所示:
class TestSendBroadcastActivity : AppCompatActivity() {
lateinit var mReceiver: TestBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_send_broadcast)
mReceiver = TestBroadcastReceiver(tv_content)
val intentFilter = IntentFilter("com.geely.interview.TEST_BROADCAST_RECEIVER")
registerReceiver(mReceiver, intentFilter)
btn_send.setOnClickListener {
val intent = Intent("com.geely.interview.TEST_BROADCAST_RECEIVER")
intent.putExtra("content", et_content.text.toString())
sendBroadcast(intent)
}
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(mReceiver)
}
}
完整效果如下所示:
示例 3:静态注册实现示例 2
上述的示例 1 和示例 2 我们都是通过动态注册广播的,这也正是推荐的方法,但是我们还是需要知识静态注册是如何使用的,我们将示例 2 改成静态注册的。首先我们将动态注册的代码去掉:
class TestSendBroadcastActivity : AppCompatActivity() {
// lateinit var mReceiver: TestBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_send_broadcast)
// mReceiver = TestBroadcastReceiver(tv_content)
// val intentFilter = IntentFilter("com.geely.interview.TEST_BROADCAST_RECEIVER")
// registerReceiver(mReceiver, intentFilter)
btn_send.setOnClickListener {
val intent = Intent("com.geely.interview.TEST_BROADCAST_RECEIVER")
intent.putExtra("content", et_content.text.toString())
sendBroadcast(intent)
}
}
override fun onDestroy() {
super.onDestroy()
// unregisterReceiver(mReceiver)
}
}
然后在 AndroidManifest.xml
中静态注册 BroadcastReceiver
,代码如下所示:
<receiver android:name=".broadcast.TestBroadcastReceiver">
<intent-filter>
<action android:name="com.geely.interview.TEST_BROADCAST_RECEIVER"/>
</intent-filter>
</receiver>
在 Android
8.0 之前,这样就可以了,但是我们前面已经说过,在 Android
8.0 系统之后,静态注册的 BroadcastReceiver
是无法接收隐式广播的,而默认情况下我们发出的自定义广播恰恰都是隐式广播。
所以我们需要调用 Intent
的 setPackage()
方法,并传入当前应用程序的包名。使得这条广播变成显示广播或者通过 Intent.setComponent(包名, 接收广播类的完整类名)
来显示的设置广播接收者。
代码如下所示:
class TestSendBroadcastActivity : AppCompatActivity() {
// lateinit var mReceiver: TestBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_send_broadcast)
// mReceiver = TestBroadcastReceiver(tv_content)
// val intentFilter = IntentFilter("com.geely.interview.TEST_BROADCAST_RECEIVER")
// registerReceiver(mReceiver, intentFilter)
btn_send.setOnClickListener {
val intent = Intent("com.geely.interview.TEST_BROADCAST_RECEIVER")
intent.putExtra("content", et_content.text.toString())
// 下面两种方法都可以
intent.setPackage(packageName)
// intent.component = ComponentName(packageName, "com.xt.interview.broadcast.TestBroadcastReceiver")
sendBroadcast(intent)
}
}
override fun onDestroy() {
super.onDestroy()
// unregisterReceiver(mReceiver)
}
}
这样我们就实现了通过静态注册来发送并接收广播。
示例 4:不同应用间广播的发送与接收
Android
中的 BroadcastReceiver
不仅能接收系统和本应用的广播,还能收到其他应用的广播,只要自定义的 Action
一样就可以了。
这里我将这个项目复杂了一份,并将包名改成不一样的。然后将这个两个应用都装到手机上,取名分别为 demo1
和 demo2
。
因为 demo1
和 demo2
的代码完全一样,所以这两个自定义广播的 Action
都是 "com.geely.interview.TEST_BROADCAST_RECEIVER
",所以当我在 demo1
中发送广播的时候,demo2
也可以接收到,并且也可以正确解析出广播所携带的数据。
效果如下所示:
可以看到当我们在 demo1
中发送广播的时候,demo2
也可以接收到,并且也成功的解析出了广播所携带的数据。
但是有些时候我们不希望自己的广播被别的 App
监听到,因为我们广播里面可能携带了一些隐式数据,这时候要怎么办呢?这时候我们就可以使用 LocalBroadcastManager
来发送应用内广播。
App
应用内广播可理解为一种局部广播,广播的发送者和接收者都同属于一个App
。- 相比于全局广播(普通广播),
App
应用内广播优势体现在:安全性高 & 效率高
示例 5:利用 LocalBroadcastManager 改写示例 4
其实这个改写很简单,只需要改写注册和发送这两个地方即可。
// 使用 LocalBroadcastManager 来注册广播
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, intentFilter)
// 使用 LocalBroadcastManager 来发送广播
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
TestBroadcastReceiver
代码如下:
class TestBroadcastReceiver: BroadcastReceiver() {
private lateinit var tvInteraction: TVInteraction
override fun onReceive(context: Context?, intent: Intent?) {
when (intent?.action) {
Intent.ACTION_AIRPLANE_MODE_CHANGED ->
Toast.makeText(context, "您的飞行模式状态改变了", Toast.LENGTH_SHORT).show()
"com.geely.interview.TEST_BROADCAST_RECEIVER" -> {
val data = intent.getStringExtra("content")
Toast.makeText(context, "接收到了自定义广播,携带的数据为$data", Toast.LENGTH_LONG).show()
// 判断延迟初始化的变量是否已经初始化了
if (::tvInteraction.isInitialized) {
tvInteraction.setText(data)
}
}
}
}
fun setTVInteractionListener(mtvInteraction: TVInteraction) {
tvInteraction = mtvInteraction
}
// 利用接口的方式将数据传递出去
interface TVInteraction {
fun setText(content: String?)
}
}
TestSendBroadcastActivity
代码如下:
class TestSendBroadcastActivity : AppCompatActivity(), TestBroadcastReceiver.TVInteraction {
private lateinit var mReceiver: TestBroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_send_broadcast)
mReceiver = TestBroadcastReceiver()
val intentFilter = IntentFilter("com.geely.interview.TEST_BROADCAST_RECEIVER")
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, intentFilter)
mReceiver.setTVInteractionListener(this)
btn_send.setOnClickListener {
val intent = Intent("com.geely.interview.TEST_BROADCAST_RECEIVER")
intent.putExtra("content", et_content.text.toString())
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
}
override fun onDestroy() {
super.onDestroy()
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver)
}
override fun setText(content: String?) {
content?.let {
tv_content.text = it
}
}
}
这样改写完以后,广播就无法被其他应用所接收了。