0%

第5章: 全局大喇叭:详解广播机制

广播机制介绍

Android中广播分为标准广播有序广播,标准广播是一种完全异步执行的广播,广播发出后,所有广播接收器机会会在同一时刻接收到广播,但同时意味着它也是无法被截断的。有序广播是一种同步执行的广播,同一时刻只有一个广播接收器能收到这条消息,当这个广播接收器的逻辑执行完毕之后才会继续传递,优先级高的广播接收器可以先收到广播,并且还可以阶段正在传递的广播,这样后面的广播接收器就收不到这条广播消息。

动态注册和静态注册广播

动态注册一般在Activity的onCreate方法中写上类似于:

1
2
3
4
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.MyReceiver");
receiver = new MyReceiver();
registerReceiver(receiver,filter);

并且在onDestroy方法中注销广播:

1
> unregisterReceiver(receiver);

然后,完善一般是内部类的MyReceiver:

1
2
3
4
5
6
class MyReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context,Intent intent){
Toast.makeText(context,"receive the broadcast",Toast.LENGTH_SHORT).show();
}
}

最后得在适当的时候发送广播:

1
2
Intent intent = new Intent("com.example.MyReceiver");
sendBroadcast(intent);

当然,如果你是用广播在APP中实现强制退出登录(如QQ账号在另一台设备上登录了),那你只需要在当前Activity上弹一个窗提示已经被强制下线即可,因此有必要将广播在BaseActivity中注册,并且在BaseActivity的onPause方法(注意不是onDestroy方法了,因为我们只需要栈顶的Activity能够响应就行)中注销广播即可。如果是接收系统级广播,可能还得在AndroidManife.xml中声明相关权限。APP中实现强制退出登录时的广播接收器可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ForceOfflineReceiver extends BroadcastReceiver{
@Override
public void onReceive(final Context context,Intent intent){
AltertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle("warning");
builder.setMessage("force offline");
builder.setCancelable(false);
builder.setPositiveButton("ok",new DialogInterface.OnclickListener(){
@Override
public void onclick(DialogInterface dialog,int which){
ActivityCollector.finishAll();//销毁所有活动
Intent intent = new Intent(context,LoginActivity.class);
context.startActivity(intent);

}
});


}
}

静态注册广播是在AndroidManife.xml中做如下的声明,其中MyReceiver类一般不是内部类,因为即使app未启动也能接收广播

1
2
3
4
5
6
7
8
9
10
11
12
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/appname">
...
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.MyReceiver"
</receiver>
</application>

如果要发送有序广播,只需要将以上发送广播的代码sendBroadcast(intent)替换成sendOrderedBroadcast(intent,null);即可。设置广播的优先级只需要设置intentFilter的priority属性即可(AndroidManifest文件中是intent-filter属性)。

使用本地广播

前面发送的广播属于系统全局广播,发出的广播可以被任何应用接收到,并且我们也可以接受来自其它任何应用发出的广播,这容易引起安全性问题,比如关键数据广播被其他应用截获,或者其他应用发送各种垃圾广播。本地广播的发送有些不同:

1
2
3
LocalBroadcastManager manager = LocalBroadcastManager.getInstance(this);
Intent intent = new Intent("com.example.MyReceiver");
manager.sendBroadcast(intent);

注册:

1
2
3
4
IntentFilter filter = new IntentFilter();
filter.addAction("com.example.MyReceiver");
localReceiver = new LocalReceiver();
manager.registerReceiver(localReceiver,filter);

同样注销广播:

1
manager.unregisterReceiver(localReceiver);

本地广播的几点优势:

  • 可以明确地知道正在发送的广播不会离开我们的程序,因此不必担心机密数据泄露。
  • 其他的程序无法将广播发送到我们程序内部,因此不用担心会有安全漏洞隐患。
  • 发送本地广播比发送系统全局广播更加高效。

另有一点需要说明:本地广播是无法通过静态注册方式来接收的,其实这也完全可以理解,因为静态注册主要就是为了让程序在未启动的情况下也能接收广播,而发送本地广播时,我们的程序肯定是已经启动了;此外,不要再onReceive方法中添加过多的逻辑或者进行任何耗时的操作,因为广播接收器中是不允许开启线程的,当onReceive方法运行了较长时间而没有结束时,程序就会报错。因此它更多的只是扮演一种打开程序其他组件的角色,如弹一条通知,或者启动一个服务等。

谢谢你的鼓励