Android Broadcast(跨进程)

1.广播broadcast
Broadcast是Android中的四大组件之一,是在组件之间传播数据的一种机制。
广播使用了观察者模式,基于消息的发布/订阅事件模型。广播的发送者和接收者事先是不需要知道对方存在的,这样使得系统的各个组件可以松耦合地组织在一起,具有高度的可扩展性,容易与其他系统进行集成。

广播的具体实现流程:
①广播接收者BroadcastReceiver通过Binder机制向AMS进行注册;
②广播发送者通过binder机制向AMS发送广播;
③AMS查找符合条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到对应的BroadcastReceiver的消息循环队列中,消息循环队列拿到广播后,回调BroadcastReceiver中的onReceive()方法。
所以广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底何时才能接收到。

2.广播的注册(广播的接受者)
BroadcastReceiver有两种注册类型:静态注册和动态注册。
①静态注册
直接在AndroidManifest.xml文件中进行注册。
<receiver android:enabled=“true”
android:exported=“true”
android:icon=“drawable resource”
android:label=“string resource”
android:name=“string”
android:permission=“string”
android:process=“string”>
< intent-filter >
< action android:name=“string” />
< /intent-filter>
< /receiver>
属性说明:
exported:此BroadcastReceiver能否接收其他App发出的广播。默认值是由receiver中有无intent-filter决定的,如果有intent-filter默认值为true,否则为false。(activity/service中的此属性默认值一样遵循此规则)。
name:此BoadcastReceiver的类名(不能是内部类,内部类只能动态注册);
permission:如果设置此属性,则只有具有相应权限的广播发送方发送的广播才能被此broadcastReceiver接收;
process:BroadcastReceiver运行所在的进程。默认为App的进程。可以指定独立的进程(Android四大基本组件都可以通过此属性指定自己的独立进程)
intent-filter:指定此广播接收器将用于接收特定Action的广播。
当App首次启动时,系统会自动实例化此BroadcastReceiver并注册到系统中。
注意:之前常说静态注册的广播接收器即使App已经退出,只要有相应的广播发出,依然可以接收到。但此种描述自Android 3.1开始有可能不再成立,具体分析见本文后面部分。
②动态注册
动态注册时无须在AndroidManifest中注册组件。直接在代码中调用Context的registerReceiver函数即可。
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)

1)首先自定义广播接收器
自定义广播接收器需要继承BroadcastReceiver并实现onReceive(context, intent)抽象方法。广播接收器接收到相应广播后会自动回调onReceive()方法。
默认情况下,广播接收器运行在UI线程中,因此onReceive方法中不能执行太耗时的操作,否则将引起ANR。在Broadcast中尽量不要处理太多逻辑问题,建议复杂的逻辑交给Activity或Service处理。一般情况下,根据实际业务需求,onReceive方法中都会涉及到与其他组件之间的交互,如发送Notification、启动service等。public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//广播接收器收到的信息
String strSMSBody = intent.getStringExtra( “SMSBody”);
String strSMSAdress= intent.getStringExtra( “SMSAdress”);
Intent myIntent = new Intent();
//通过Intent把广播接收器收到的信息发给新的Activity
myIntent.putExtra(“SMSBody”, strSMSBody);
myIntent.putExtra(“SMSAdress”, strSMSAdress);
//从Service或BroadcastReciver往Activity跳转时,要将Intent的flag设置为FLAG_ACTIVITY_NEW_TASK才可以
myIntent.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK);
myIntent.setClass(context, BroadcastActivity.class);
context.startActivity(myIntent);
}
}

2)注册广播
public class MainActivity extends Activity {
public static final String BROADCAST_ACTION = “com.demo.broadcast”;
private BroadcastReceiver mBroadcastReceiver;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//动态注册广播
mBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction( BROADCAST_ACTION);
registerReceiver(mBroadcastReceiver, intentFilter, “com.demo.test.permission”, null);
}
}
Activity实例化时会动态将MyBroadcastReceiver注册到系统中。当Activity销毁时,动态注册的MyBroadcastReceiver将不会再接收到相应的广播。
注意:Android中所有与观察者模式有关的设计中,一旦涉及到register,必定在相应的时机需要unregister。因此在onDestroy()中需要unregisterReceiver(mBroadcastReceiver)。

3.广播的发送(广播发送者)
通常说”发送广播“和”接收广播“,表面上看广播作为Android广播机制的实体,实际上这一实体本身并不是以所谓的”广播“对象存在的,而是Intent表示。广播的定义过程实际就是相应广播intent的定义过程,然后通过广播发送者将此intent发送出去,被相应的BroadcastReceiver接收后回调onReceive()函数。
根据广播的发送方式,广播可以分为几种类型:
Normal Broadcast:普通广播
System Broadcast:系统广播
Ordered broadcast:有序广播
Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用)
Local Broadcast:App应用内广播

各种类型的广播发送方式及其特点:
①Normal Broadcast普通广播
将Intent通过sendBroadcast(intent, …)发送。具体方法有:
sendBroadcast(intent);
sendBroadcast(intent, receiverPermission);
sendBroadcastAsUser(intent, userHandler);
sendBroadcastAsUser(intent, userHandler, receiverPermission);
普通广播会被已注册的且intent-filter匹配的BroadcastReceiver接收,且顺序是无序的。
如果发送广播时有权限要求,BroadcastReceiver想要接收此广播也需要有相应的权限。
普通广播是完全异步的,可以在同一时刻被所有接收者接收到,消息传递的效率比较高。但缺点是接收者不能将处理结果传递给下一个接收者,并且无法终止广播Intent的传播。

//发送广播
Intent it = new Intent();
it.setAction(BROADCAST_ACTION_NORMAL);
it.putExtra(“name”,“normal broadcast”);
sendBroadcast(it,“com.demo.tst.permission”);

②System Broadcast系统广播
Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本都会发出相应的系统广播。如开机启动、网络状态改变、拍照、屏幕关闭与开启、电量不足等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时由系统自动发出。

③Ordered broadcast有序广播
有序广播中的“有序”是针对广播接收者而言,指的是发送出去的广播被BroadcastReceiver按先后顺序接收。
有序广播是一种同步执行的广播,在广播发出之后,同一时刻只会有一个广播接收器能接收到这条广播消息,当这个广播接收器中的逻辑执行完后广播才会继续传递。所以此时的广播接收器是有先后顺序的,优先级高的广播接收器就可以先收到广播消息,并且前面的广播接收器还可以修改甚至截断正在传递的广播。
有序广播的定义过程与普通广播相同,只是其发送方式变为:sendOrderedBroadcast(intent, receiverPermission, …)。
对于有序广播,主要特点有:
a.当前多个已经注册且有效的BroadcastReceiver接收有序广播时是按先后顺序接收的,先后顺序判定标准遵循:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值(声明在intent-filter元素的android:priority属性中,数越大优先级别越高,取值范围:-1000到1000,也可以调用IntentFilter对象的setPriority()进行设置)从大到小排序,对于具有相同priority的动态广播和静态广播,动态广播会排在前面。
b.先接收的BroadcastReceiver可以通过BroadcastReceiver.abortBroadcast()方法对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播;也可以将处理结果通过setResultExtras(Bundle)存放进结果对象再传给下一个接收者,下一个接收者通过Bundle bundle =getResultExtras(true)可以获取上一个接收者存入在结果对象中的数据。一个经典的应用案例是电话黑名单,首先通过将黑名单号码保存在数据库里面,当来电时将会接收到来电广播并将黑名单号码与数据库中的某个数据做匹配,如果匹配的话则做出相应的处理,比如挂掉电话或静音等。
//广播注册
mBroadcastReceiver1 = new MyBroadcastReceiver1();
IntentFilter intentFilter1 = new IntentFilter();
intentFilter1.addAction(BROADCAST_ACTION_ORDERED);
intentFilter1.setPriority(800);
registerReceiver(mBroadcastReceiver1, intentFilter1);

mBroadcastReceiver2 = new MyBroadcastReceiver2();
IntentFilter intentFilter2 = new IntentFilter();
intentFilter2.addAction(BROADCAST_ACTION_ORDERED);
intentFilter2.setPriority(500);
registerReceiver(mBroadcastReceiver2, intentFilter2);

mBroadcastReceiver3 = new MyBroadcastReceiver3();
IntentFilter intentFilter3 = new IntentFilter();
intentFilter3.addAction(BROADCAST_ACTION_ORDERED);
intentFilter3.setPriority(300);
registerReceiver(mBroadcastReceiver3, intentFilter3);

//取消注册广播
unregisterReceiver(mBroadcastReceiver1);
unregisterReceiver(mBroadcastReceiver2);
unregisterReceiver(mBroadcastReceiver3);

//广播发送
Intent it = new Intent();
it.setAction(BROADCAST_ACTION_ORDERED);
it.putExtra(“name”,“ordered broadcast”);
sendOrderedBroadcast(it, null);

//广播接收
public class MyBroadcastReceiver1 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String name = intent.getStringExtra(“name”);
text2_1.setText(name);
showlog(“name=”+name);
Bundle bundle = new Bundle();
bundle.putString(“name”, “ordered broadcast modified”);
setResultExtras(bundle);
}
}

public class MyBroadcastReceiver2 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获取原生广播的数据
//String name = intent.getStringExtra(“name”);
//获取被上一个接收者修改后的数据
Bundle bundle =getResultExtras(true);
String name = bundle.getString(“name”);
text2_2.setText(name);
showlog(“name=”+name);
bundle.putString(“name”, “ordered broadcast modified again”);
setResultExtras(bundle);
// abortBroadcast(); //终止广播
}
}

public class MyBroadcastReceiver3 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//获取原生广播的数据
//String name = intent.getStringExtra(“name”);
//获取被上一个接收者修改后的数据
Bundle bundle =getResultExtras(true);
String name = bundle.getString(“name”);
text2_3.setText(name);
showlog(“name=”+name);
}
}
点击按钮,发送广播,看Log输出信息:
在这里插入图片描述
Receiver1和Receiver2对接收到的数据分别进行了修改,继续传递给后续的广播接收器。
注意:通过setResultExtras(bundle)传递的数据不会更改原生广播的数据,只是在原来广播数据中额外添加新的数据。

④Sticky Broadcast粘性广播
在android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播同样已经废弃。

⑤Local BroadcastApp应用内广播(此处的App应用以App应用进程为界)
Android中的广播可以跨进程甚至跨App直接通信,且注册时exported对于有intent-filter的情况下默认值是true,这样可能出现安全隐患:
a.其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;
b.其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。
无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:
a.对于同一App内部发送和接收广播,将exported属性主动设置成false,使得非本App内部发出的此广播不被接收;
b.在广播发送和接收时都增加上相应的permission权限,用于权限验证;
c.发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

Local Broadcast可以理解成一种局部广播的形式,广播的发送者和接收者都属于同一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时之所以使用应用内广播,而不使用全局广播的形式,更多考虑到的是Android广播机制中的安全性问题。
相比于全局广播,App应用内广播优势体现在:安全性更高、更加高效。
为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将context变成LocalBroadcastManager的单一实例。
//注册应用内广播接收器
mBroadcastReceiver4 = new MyBroadcastReceiver4();
IntentFilter intentFilter4 = new IntentFilter();
intentFilter4.addAction(BROADCAST_ACTION_LOCAL);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver4, intentFilter4);

//取消注册应用内广播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver4);

//发送应用内广播
Intent it = new Intent();
it.setAction(BROADCAST_ACTION_LOCAL);
it.putExtra(“name”,“local broadcast”);
localBroadcastManager.sendBroadcast(it);

//接收应用内广播
public class MyBroadcastReceiver4 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
showlog(“MyBroadcastReceiver4-onReceive”);
String name = intent.getStringExtra(“name”);
text3.setText(name);
showlog(“name=”+name);
}
}
注意:对于LocalBroadcastManager方式发送的应用内广播,只能通过LocalBroadcastManager动态注册的BroadcastReceiver才有可能接收到(静态注册是接收不到的)。

5.API重要变迁
前面说“静态注册的广播接收器即使app已经退出,只要有相应的广播发出依然可以接收到,但这种描述自Android 3.1开始有可能不再成立”。这里探究一下原因:
Android 3.1开始系统在Intent与广播相关的flag增加了参数:①FLAG_INCLUDE_STOPPED_PACKAGES:包含已经停止的包(即包所在的进程已经退出)
②FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已经停止的包
自Android3.1开始,系统本身增加了对所有app当前是否处于运行状态的跟踪。在发送广播时,不管是什么广播类型,系统默认直接增加了值为FLAG_EXCLUDE_STOPPED_PACKAGES的flag,导致即使是静态注册的广播接收器,对于其所在进程已经退出的app,同样无法接收到广播。

对于系统广播,由于是系统内部直接发出,无法更改此intent flag值。因此3.1开始对于静态注册的接收系统广播的BroadcastReceiver,如果App进程已经退出,将不能接收到广播。
但是对于自定义的广播,可以通过复写此flag为FLAG_INCLUDE_STOPPED_PACKAGES使得静态注册的BroadcastReceiver,即使所在App进程已经退出,也能接收到广播,并会启动应用进程,但此时的BroadcastReceiver是重新新建的。
Intent intent = new Intent();
intent.setAction(BROADCAST_ACTION);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.putExtra(“name”, “test broadcast”);
sendBroadcast(intent);
注意:
①对于动态注册的BroadcastReceiver,由于注册和取消注册是在其他组件(如Activity)中进行,因此,不受此改变影响。
②在3.1以前,相信不少app可能通过静态注册方式监听各种系统广播,以进行一些业务上的处理(如即时app已经退出,仍然能接收到,可以启动service等…),3.1后静态注册接收广播方式的改变,将直接导致此类方案不再可行。于是通过将Service与App本身设置成不同的进程已经成为实现此类需求的可行替代方案。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值