本系列原本写于博客园,现移植到自己的博客上并重新编辑。
一、广播的功能和特征
- 广播的生命周期很短,经过 调用对象—实现onReceive—结束 整个过程就结束了。从实现的复杂度和代码量来看,广播无疑是最迷你的Android组件,实现往往只需几行代码。广播对象被构造出来后通常只执行
BroadcastReceiver.onReceive
方法,便结束了其生命周期。所以有的时候我们可以把它当做函数看也未必不可。 - 和所有组件一样,广播对象也是在应用进程的主线程中被构造,所以广播对象的执行必须是要同步且快速的。也不推荐在里面开子线程,因为往往线程还未结束,广播对象就已经执行完毕被系统销毁。如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成。
- 每次广播到来时,会重新创建
BroadcastReceiver
对象 , 并且调用onReceive()
方法 , 执行完以后 , 该对象即被销毁 . 当onReceive()
方法在 10 秒内没有执行完毕, Android 会认为该程序无响应。
二、广播事件监听的两种方法
使用广播进行事件监听有两种方法,静态注册和动态注册,又或者称冷插拔和热插拔。静态注册就是将广播接收器的相关信息写在应用的配置文件中。当有广播事件发生时,组件管理服务就会从安装包管理服务中获取已安装应用的广播组件信息。动态注册则是通过Context.registerReceiver和Context.unregisterRecever,动态将广播接收器与所需要监听的事件绑定。
静态注册
首先是在应用的配置文件中写入注册消息,同样是跟其他组件一样写在application标签之内1
2
3
4
5
6<receiver android:name=".ColdReceiver"><!-- 你的Receiver名称 -->
<intent-filter>
<action android:name="android.intent.action.COLD_BROADCAST"/> <!-- 你广播要接受的intent名称 -->
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
然后我们建一个ColdReceiver的类,继承BroadcastReceiver,里面代码如下1
2
3
4
5
6
7
8
9
10
11
12
13
14public class ColdReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
//跳转到service中
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(1);
//开启service
context.startService(intent);
//日志打印
Log.d("TEST","静态注册");
}
}
其中service的配置和内容如下1
2
3
4
5
6<service android:name=".BroadcastService"><!-- 你自定义的service文件 (在<application></application>里面加)-->
<intent-filter>
<action android:name="android.intent.action.BroadcastService" /><!-- 用intent启动时的快捷名(也可以用常规的方式启动) -->
<category android:name="android.intent.category.default" />
</intent-filter>
</service>
1 | public class BroadcastService extends Service{ |
静态广播的创建就此完成,简单的两个步骤,一是配置广播,二是继承BroadcastReceiver,重写里面的onReceive函数。
接下来我们再新建一个工程来检测广播是否可以响应消息。(检测应用的代码全部都在下方,并且不用再做更改了)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60public class MainActivity extends Activity implements OnClickListener{
private Button b1,b2,b3,b4;
private Intent intent;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent();
//获得界面的控件
b1 = (Button) findViewById(R.id.button1);
b1.setOnClickListener(this);
b2 = (Button) findViewById(R.id.button2);
b2.setOnClickListener(this);
b3 = (Button) findViewById(R.id.button3);
b3.setOnClickListener(this);
b4 = (Button) findViewById(R.id.button4);
b4.setOnClickListener(this);
Log.d("TEST","===初始化完成===");
}
public void onClick(View v) {
// TODO Auto-generated method stub
switch(v.getId()){
case R.id.button1:{//发送到静态注册广播
intent = new Intent("android.intent.action.COLD_BROADCAST");
sendBroadcast(intent);
//intent.putExtra("msg", "hello coldreceiver.");
break;
}
case R.id.button2:{//发送到动态注册广播
intent = new Intent("android.intent.action.HOT_BROADCAST");
//intent.putExtra("msg", "hello hotreceiver.");
sendBroadcast(intent);
break;
}
case R.id.button3:{//普通广播
intent = new Intent("android.intent.action.NORMAL_BROADCAST");
sendBroadcast(intent);
break;
}
case R.id.button4:{//有序广播
intent = new Intent("android.intent.action.SORT_BROADCAST");
sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");
break;
}
}
}
public void show(String str){
Toast.makeText(this, str, Toast.LENGTH_LONG).show();
}
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
}
ok,将两个应用都安装到设备上,启动测试用的应用,点击第一个按钮,运行的效果如下
同时,会出现”静态注册”的Toast(不方便截图)。可以看出静态注册广播能够跨应用来响应信息,这都要归功于安卓上的组件管理服务,它会读取每个应用的配置文件,然后获取里面的组件信息,每当有消息响应时,组件管理服务会从中查找有没有需要调用的组件,并判断是否进行执行。
动态注册
动态注册也可以分成两部分,一在代码中进行动态注册,二还是继承BroadcastReceiver,重写里面的onReceive函数。
我们在广播应用中新建一个HotReceiver,继承BroadcastReceiver1
2
3
4
5
6
7
8
9
10
11
12public class HotReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
//String msg = intent.getStringExtra("msg");
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(2);
context.startService(intent);
Log.d("TEST","动态注册");
}
}
在Activity中进行动态注册1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public class MainActivity extends Activity {
private HotReceiver receiver;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册广播
//新建一个广播对象
receiver = new HotReceiver();
//新建一个intent管理机制,(功能是对组件进行过滤,只获取需要的消息)
IntentFilter filter = new IntentFilter();
//添加白名单(只获取该动作的信息)
filter.addAction("android.intent.action.HOT_BROADCAST");
//与广播绑定,进行注册
registerReceiver(receiver, filter);
}
protected void onDestroy() {
//取消注册,一定要记得,不然系统会报错误
unregisterReceiver(receiver);
stopService(new Intent("android.intent.action.BroadcastService"));
super.onDestroy();
}
}
ok,再使用测试应用来检查一下效果,注意步骤,安装好广播应用打开,不要让它退出,切换到测试用的广播,点击第二个按钮。
测试成功。那么我们关掉广播应用在测试一下,会发现不会再出现动态注册的打印消息。这说明动态注册的广播是与Activity绑定的,当Activity销毁时,广播也会被销毁。
在Android中,很多时候最好是使用动态注册的方式使用广播,比如时间变化事件,电量变更事件等,这些事件触发率太高,如果使用静态注册,会导致进程频繁的被构造和销毁从而影响整个系统的效率。
三、广播的两种类型
普通广播
普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。
接下来我们新建三个广播来进行验证。1
2
3
4
5
6
7
8
9
10
11
12public class NormalReceiver1 extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(3);
context.startService(intent);
Log.d("TEST","普通广播1");
abortBroadcast();
}
}
1 | public class NormalReceiver2 extends BroadcastReceiver { |
1 | public class NormalReceiver3 extends BroadcastReceiver { |
1 | <receiver android:name=".NormalReceiver1"> |
安装完毕后,我们点击检测应用的第三个按钮,可以看到abortBroadcast()
作用是阻断广播向下一级传播,显然在这里不起作用,并会让系统报错。所以如果要让广播有一定的优先级进行传播就要使用到有序广播。
有序广播
有序广播通过调用sendOrderedBroadcast函数进行发送。它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播。在有序广播的传递过程中,每个执行中的触发器组件都可以通过BroadcastReceiver.setResult等函数附加额外的数据,而下一个广播则可以使用这些数据(BroadcastReceiver.getResultData)。这样可以构成一个消息数据处理链。
为了保证某一事件一定会被处理,可以指明默认的广播接收器(Final Receiver)。
一样的,新建三个广播样例1
2
3
4
5
6
7
8
9
10
11
12
13
14public class SortReceiver1 extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
//String msg = intent.getStringExtra("msg");
intent = new Intent("android.intent.action.BroadcastService");
intent.addFlags(4);
context.startService(intent);
Log.d("TEST","有序广播1");
abortBroadcast();
}
}
1 | public class SortReceiver2 extends BroadcastReceiver { |
1 | public class SortReceiver3 extends BroadcastReceiver { |
1 | <receiver android:name=".SortReceiver1"> |
我们看到,现在这三个接收者的
同样发送广播的代码也是不一样的1
sendOrderedBroadcast(intent, "scott.permission.SORT_BROADCAST_PERMISSION");
注意,使用sendOrderedBroadcast
方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为 null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信 的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。
所以我们在AndroidMainfest.xml中定义一个权限,并获得权限:(是要在广播的应用中声明)1
2
3<permission android:protectionLevel="normal"
android:name="scott.permission.SORT_BROADCAST_PERMISSION" />
<uses-permission android:name="scott.permission.SORT_BROADCAST_PERMISSION" />
(这里不是写在application内部,而是同application同级)
运行后只会出现这么一个消息:
因为在第一个广播出我们就终止了广播的继续传递,所以就只会出现这么一条打印消息。
四、形形色色的广播
资源下载:BroadcastDemo