今天带来 Android 第三大组件——Broadcast Receiver,可以用于和自己或者其他 App 发送消息,和 Service 一样,它也没有自己的 UI 界面。Broadcast Receiver 的设计模式非常类似发布/订阅模式,可用于做进程间通信(IPC)。通过本节的学习你可以详细的了解到 Android 中广播的作用和使用方法,并且能够将其灵活的运用到实际开发中。
1. 什么是广播接收器
首先看看官方文档的部分解释:
Base class for code that receives and handles broadcast intents sent by Context.sendBroadcast(Intent).
You can either dynamically register an instance of this class with Context.registerReceiver() or statically declare an implementation with the tag in your AndroidManifest.xml.
简单翻译如下:
Broadcast Receiver 是一个基类,继承自它的类可以用来处理通过Context.sendBroadcast(Intent)发送过来的 intent 信息。与其他组件不同的是,Broadcast Receiver 支持动态注册,即你可以在代码里调用Context.registerReceiver()来注册广播接收器,也可以和 Activity、Service 一样使用静态注册的方式,即在 AndroidManifest.xml 文件中进行注册。
这是从“Broadcast Receiver”文档里面截取的一部分,解释的不是很全面。简而言之就是 Android 系统为我们提供了各式各样系统广播,用来给所有 App 推送一些全局消息,如:网络切换、电量变化、耳机插拔、蓝牙断开等等。如果 App 希望获得这些事件,那么可以提前注册一个广播接收器,然后就能收到相应类型的广播了。同时我们还可以自定义一些类型,比如上一节中的音乐播放器,我们可以定义“切歌”为一个广播事件,然后可供其他 App,或者 App 内其他页面去接收。
2. 系统类型广播
在学习自定义广播之前,我们先来看看一些常用的系统广播,通常都是发送一些系统相关的事件:
- android.intent.action.BATTERY_CHANGED:
- 通知电量变化,可以获取电量百分比等相关数据
- android.intent.action.BATTERY_LOW:
- 低电通知
- android.intent.action.POWER_CONNECTED:
- 充电器连接
- android.intent.action.POWER_DISCONNECTED:
- 充电器断开
- android.intent.action.BOOT_COMPLETED:
- 设备启动
- android.intent.action.DATE_CHANGED:
- 设备时间修改
- android.intent.action.REBOOT:
- 设备重启
- android.intent.action.CONNECTIVITY_CHANGE:
- 网络环境变化,可以从“intent”中解析出具体的网络类型
- android.intent.action.BUG_REPORT:
- bug 上报事件
- android.intent.action.CALL_BUTTON:
- 用户点击的拨打电话按键
3. Broadcast 的类型
在 Android 中有两种类型的广播:
- 有序广播
- 无序广播
下面分别来介绍这两类广播。
3.1 有序广播
顾名思义,有序广播是按照一定的顺序同步进行发送的,也就是每一个 Broadcast Receiver 是按照顺序一个一个的收到消息。而这个顺序优先级依赖于android:priority属性,我们可以在 AndroidManifest.xml 中注册的时候配置优先级,当然优先级越高的 Receiver 会更早的接收到广播,同优先级的 Receiver 顺序随机。
在这种情况下,一个广播在一个时间点只会被发送给一个 Receiver。当一个 Receiver 收到广播只会,它可以决定让这条广播继续向更低优先级的 Receiver 发送,或者可以拦截掉这条 Receiver,换言之,高优先级的 Receiver 可以决定低优先级的 Receiver 是否还能收到广播。
3.2 无序广播
理解了有序广播之后,无序广播就很容易理解了。无需广播就是没有任何优先级之分,所有广播都是异步同时发送,此时 Receiver 的接收时序也是完全随机的。正因为异步且无需保证时序,所以无需广播更高效,但是不能在各个 Receiver 之间传递数据。
4 Broadcast Receiver 的使用示例
为了打造一个更好的 App,我们需要充分的发挥广播的优势,因为它足够灵活,足够强大。
下面我们一起写一个通过 Broadcast Receiver 发送消息的例子,用户在输入框输入的消息,我们通过广播发送出去,最后在广播接收器中进行解析并打印输入的内容。
4.1 注册
Broadcast Receiver 是四大组件中唯一一个支持动态注册的组件,我们可以在代码中通过Context.registerReceiver()
方法进行注册:
IntentFilter filter = new IntentFilter(); intentFilter.addAction(getPackageName()+"com.emercy.CUSTOM_RECEIVER"); MyReceiver myReceiver = new MyReceiver(); registerReceiver(myReceiver, filter);
同时,也支持类似 Activity、Service 的静态注册方式,在 AndroidManifest.xml 中添加以下注册代码:
<receiver android:name=".MyBroadcastReceiver"> <intent-filter> <action android:name="com.emercy.CUSTOM_RECEIVER" /> </intent-filter> </receiver>
4.2 广播接收器
注册之后我们就可以放心的编写我们的广播接收器了,方法很简单:首先创建一个类继承自BroadcastReceiver,然后覆写onReceive(Context context, Intent intent)方法,其中广播传递的参数就在 intent 当中,我们可以在 onReceive() 方法中取出,代码如下:
package com.emercy.myapplication; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyBroadcastReceiver extends BroadcastReceiver { static final String BROADCAST_INTENT = "com.emercy.CUSTOM_RECEIVER"; @Override public void onReceive(Context context, Intent intent) { CharSequence data = intent.getCharSequenceExtra("msg"); Toast.makeText(context, "接收到的消息: " + data, Toast.LENGTH_LONG).show(); } }
通过intent.getCharSequenceExtra()
方法获取广播中的参数,然后通过 Toast 进行打印即可。
4.3 布局文件编写
布局文件主要有两个关键点:
- 添加一个输入框 EditText,用于接收用户的输入;
- 添加一个 Button,用于触发广播的发送,同时获取 EditText 中输入的内容放入 intent 中。如下:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginStart="100dp" android:layout_marginTop="150dp" android:text="广播接收器" android:textColor="#a4c639" android:textSize="50sp" /> <EditText android:id="@+id/textMsg" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="100dp" android:layout_marginTop="270dp" android:ems="10" /> <Button android:id="@+id/send" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="130dp" android:layout_marginTop="310dp" android:text="发送广播" /> </RelativeLayout>
4.4 发送广播
MainActivity 主逻辑的任务就很明显了,接收用户的输入,塞入 intent 并通过 Broadcast 发送出去:
package com.emercy.myapplication; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; import static com.emercy.myapplication.MyBroadcastReceiver.BROADCAST_INTENT; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.send).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { EditText st = findViewById(R.id.textMsg); // 将EditText的内容塞到Intent里,通过广播发出去 Intent intent = new Intent(); intent.putExtra("msg", (CharSequence) st.getText().toString()); intent.setAction(BROADCAST_INTENT); sendBroadcast(intent); } }); } }
编译运行,在 EditText 中输入任意字符,然后点击“发送广播”,此时 MyBroadcastReceiver 中的 onReceive()
方法被触发,从而将我们输入的内容 Toast 出来,如图:
5 小结
本节学习的是 Android 第三个组件,Android 预置了很多个系统广播,主要用于让 App 很方便的接收一些系统事件。广播分为有序广播和无序广播,有序广播会按照优先级顺序依次发送给 Receiver,而无序广播则是同时发送,相比之下无序广播的效率更高,但是不可控。如果希望自定义广播,可以直接创建一个 BroadcastReceiver 的子类,覆写onReceive(),这样就可以从 intent 中取到数据了。使用广播,可以很方便的在应用内、应用间传递消息,对功能解耦、事件传递等场景非常适用,你学会了吗?