第六章 组件通讯与广播消息( 普通广播、有序广播、粘性广播、本地广播

参考资料:
《Android应用程序开发》ISBN 9787302283164
参考软件:
Android Studio、Eclipse+ADT、Android SDK、JDK
普通广播
普通广播是完全异步执行的广播。该广播没有任何的顺序可言,因此效率比较高。也意味着它无法被截断。广播的工作流程如下:

广播之间不能传递数据,不能终止广播
这是一种不需要考虑接收者接收顺序的广播,比如说有3个接收机,都关注custom.action.mybroadcast这种广播,无所谓谁先收到谁后收到。接收机不能阻止其它接收机获取到这条广播。

(1)、我们创建一个名为GostBroadcast的类继承BroadcastReceiver,实现一个自定义的广播。


public class GostBroadcast extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String data = intent.getStringExtra("data");
Toast.makeText(context, data + "/cast", Toast.LENGTH_LONG).show();
}
}
(2)、注册广播,我们注册一个应用程序级别的广播,在manifest文件中进行注册。注意在注册广播的时候需要指定该广播的action
<receiver android:name="com.dsw.servicedemo.GostBroadcast"
android:exported="false">
<intent-filter>
<action android:name="com.dsw.send"/>
</intent-filter>
</receiver>
(3)、发送广播,通过Intent进行广播的启动发送。
Intent intent = new Intent();
intent.setAction("com.dsw.send");
intent.putExtra("data", "data");
sendBroadcast(intent);
通过这样一个流程,我们就完成了一个广播从创建、发送、接收处理的整个过程。最后在页面弹出Toast展示我们传递的数据。
有序广播
有序广播是同步执行的,广播可以被中断。
有序广播是用sendOrderedBroadcast来发送。高优先级的接收者会先接收到广播,然后它可以决定是否继续转发,让低优先级的接收者接收到,或者终止广播。高优先级的接收者可以通过setResult把一些信息传给下一个接收者,下一个接收者则通过getResult获取上一个接收者传过来的信息。这个优先级也是用android:priority来设置,范围是-1000到1000。

这是一种需要考虑接收者接收顺序的广播,比如说有3个接收机,都关注custom.action.mybroadcast这种广播,那么安卓系统将根据这3个接收机声明的优先级进行广播的投递。
而且有序广播是可以被阻截的。
比如,一个广播按照顺序传递给3个接收机-A B C,但是B将广播拦截了,因此C将不会收到这个广播。

1 发送
Intent i = new Intent("custom.action.mybroadcast");
sendOrderedBroadcast(i, null);
2 接收
接收的时候,需要给intent-filter标签设置android:priority属性,表示这个接收机的优先级。优先级从-1000到1000,数值越大,优先级越高。
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="true">
<intent-filterandroid:priority="1000">
<actionandroid:name="custom.action.mybroadcast"/>
<categoryandroid:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
接收到广播以后,Broadcast Receiver可以将广播拦截,禁止它往下传播,
publicclassMyReceiverextendsBroadcastReceiver {
publicMyReceiver() {
}
//实现onReceive接口
@Override
publicvoidonReceive(Context context, Intent intent) {
//禁止往下传播
abortBroadcast();
}
}
如果接收机1在onReceive()中,希望把数据传递给下个接收机2,
接收机1可以使用setResultExtras()方法,
publicclassMyReceiver1extendsBroadcastReceiver {
publicMyReceiver() {
}
@Override
publicvoidonReceive(Context context, Intent intent) {
Bundle b = new Bundle();
b.putString("data", "this data from MyReceiver");
setResultExtras(b);
}
}
在接收机2中,
publicclassMyReceiver2extendsBroadcastReceiver {
publicMyReceiver2() {
}
@Override
publicvoidonReceive(Context context, Intent intent) {
Bundle b = getResultExtras(true);
if(b!=null)
{
//data就是前一个接收机1传来的-this data from MyReceiver
String data = b.getString("data");
}
}
}
假如希望将数据放到onReceive()传入的Intent当中,是不会传递成功的,
@Override
publicvoidonReceive(Context context, Intent intent) {
//这是不会成功的
intent.putExtra("data", "this data from MyReceiver");
}
传递数据,一定要通过BroadcastReceiver提供的setResultExtras()方法。
粘性广播
在Android系统粘性广播一般用来确保重要的状态改变后的信息被持久保存,并且能随时广播给新的广播接收器,比如电源的改变,因为耗电需要一个过程,前一个过程必须提前得到,否则可能遇到下次刚好接收到的广播后系统自动关机了,随之而来的是kill行为,所以对某些未处理完的任务来说,后果很严重。
发送粘性广播需要权限(这里的权限是保存信息的权限和由系统发送未处理的广播的权限)
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
对于粘性广播的发送,和普通广播的发送方式是一致的。为了测试粘性广播的正确使用方式,我们需要定义一个SenderActivity来发送stick Broadcast
public class SenderActivity{@Overrideprotected void onCreate(Bundle saveInstance) { super.onCreate(saveInstance); setContentView(R.layout.stcik_test_layout); getWindow().setBackgroundDrawableResource(R.drawable.avatar11);}@Overrideprotected void onResume() { super.onResume();getWindow().getDecorView().postDelayed(new Runnable() { @Override public void run() { sendStickyBroadcast(); } }, 3*1000);}private void sendStickyBroadcast(){ Intent i = new Intent(); i.setAction(StickyBroadcastReceiver.Action); i.putExtra("info", "sticky broadcast has been receiver"); sendStickyBroadcast(i); Log.i("Other","sticky broadcast send ok!");}}
需要知道的是粘性广播是普通广播的一种,因此也可以使用普通广播接收器来接收,当然粘性广播还有另一种常用的接收方式。
1.使用普通广播接收器,注意,必须在Manifiest或者发送之前接收(这和定义有点违背,因为这种方式不是正确的接收方式)
public class StickyBroadcastReceiver extends BroadcastReceiver { public static final String Action = "com.sample.test.sticky.broadcast.receiver"; public static final String PERMISSION = "com.sample.test.permission.sticky.receiver"; @Override public void onReceive(Context context, Intent intent) { int checkCallingOrSelfPermission = context.checkCallingOrSelfPermission(PERMISSION); if(PackageManager.PERMISSION_GRANTED == checkCallingOrSelfPermission) //权限判断 { Toast.makeText(context, "授权成功", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(context, "授权失败", Toast.LENGTH_SHORT).show(); throw new RuntimeException("permission denied"); } if(intent!=null&&Action.equals(intent.getAction())) { Toast.makeText(context, intent.getStringExtra("info"), Toast.LENGTH_SHORT).show(); } }}
Mainifest.xml
<!--自定义权限--><permission android:name="com.sample.test.permission.sticky.receiver" android:protectionLevel="normal" /><!--使用自定义权限--><uses-permission android:name="com.sample.test.permission.sticky.receiver"/><!--使用粘性广播发送权限--><uses-permission android:name="android.permission.BROADCAST_STICKY" /><!--省略一部分--><receiver android:name="test.view.weitop.home.StickyBroadcastReceiver" android:permission="com.sample.test.permission.sticky.receiver" > <!--android:permission 其他应用使用时,需要检测的权限--> <intent-filter > <action android:name="com.sample.test.sticky.broadcast.receiver"/> </intent-filter></receiver>
2.使用正确的方式接收(推荐)
正确的接收方式不应该使用BroadcastReceiver就可以接收到
只需要将SenderActivity的onResume稍作修改即可
@Overrideprotected void onResume() { super.onResume();//3秒后发送 getWindow().getDecorView().postDelayed(new Runnable() { @Override public void run() { sendStickyBroadcast(); } }, 3*1000);//15秒后就收getWindow().getDecorView().postDelayed(new Runnable() { @Override public void run() { IntentFilter intentFilter = new IntentFilter(StickyBroadcastReceiver.Action); Intent data = registerReceiver(null, intentFilter); if(data!=null&&StickyBroadcastReceiver.Action.equals(data.getAction())) { Toast.makeText(this, data.getStringExtra("info"), Toast.LENGTH_SHORT).show(); } } }, 15*1000);}
在这样一个相差10秒左右的时间段 【先发送,后接收】,说明了粘性广播的已经将信息完全保存起来了,只要我们去使用如下方式,即可获取到,而且无限次获取。
Intent data = registerReceiver(null, intentFilter); //注意,不需要接收器,否则可能无法接收到
附注:这种广播也可以被移除 ,我们可以接收到 广播后调用 removeStickyBroadcast(intent);
本地广播
前面所发送和接收的所用广播都属于系统的全局广播,我们发出的这些广播可以给系统中任何应用程序接收到,当然我们也可以接受其他应用程序的广播。但是,只用本地广播机制发出的广播只能在本应用程序中接收到。这样的话安全性得到了提升。本地广播机制主要是用LocalBroadcastManager来管理,它提供了发送广播的方法sendBroadcast()和sendBroadcastSync()(这也方法好像用的不多,我暂时没有深究,等以后真正用到可以查一下),注册接收者的方法registerReceiver(localReceiver, localIntentTilter),需要两个参数,取消注册的方法localBroadcastManager.unregisterReceiver(localReceiver);
下面看一下本地广播机制的工作流程:
1.发送本地广播
//发送本地广播localBroadcastManager = LocalBroadcastManager.getInstance(this); //获取本地广播管理实例localCastBtn = (Button) findViewById(R.id.local_btn);localCastBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //发送本地广播 Intent intent = new Intent("com.zwf.broadcastdemo.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent); }});
2.注册广播接收者
//注册本地广播监听localIntentTilter = new IntentFilter("com.zwf.broadcastdemo.LOCAL_BROADCAST");localReceiver = new LocalReceiver();localBroadcastManager.registerReceiver(localReceiver, localIntentTilter);
3.取消注册
//取消本地广播监听localBroadcastManager.unregisterReceiver(localReceiver);
使用本地广播的优势
1. 可以明确地知道正在发送的广播不会离开本程序,因此不需要担心机密数据泄漏的问题。
2. 其他程序无法将广播发送到本程序的内部,因此不需要担心会有安全漏洞的隐患。
3. 发送本地广播比起发送系统全局广播将会更加高效。