android-基础知识-Broadcast
概述
主要写 1.广播的分类 2.广播的优缺点 3.广播的实现原理
广播的分类
1.有序广播 无序广播2.静态广播 动态广播 3.本地广播 4.系统广播 5.粘性广播(Sticky Broadcast:粘性广播(在 android 5.0/api 21中deprecated,不再推荐使用,相应的还有粘性有序广播,同样已经deprecated)。)
1.有序广播、无序广播
1.1 无序广播(标准广播)
无序广播又叫标准广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播的信息,因此他们之间没有任何的先后顺序可言
sendBroadcast(intentBro, "receiverPermission");
其中"receiverPermission" 参数为非必要。
1.2. 有序广播:
有序广播接收器是有先后顺序的,而且前面的广播可以对后续的广播进行截断,以阻止让其继续广播。权限高者会先获取得到广播的信息
sendOrderedBroadcast(intentBro, "receiverPermission");
其中"receiverPermission" 参数为必要。
问题1:为啥两种方式receiverPermission一个可以无一个必须有呢?
回答1:
- 先说“receiverPermission”参数是干啥的。它是“没有相同权限的其他进程不可以接收到,本进程内不受影响没有此权限的也可以接受到”,当避免其他进程接收的时候可以用它。
- 因为有序广播存在高优先级priority拦截低priority的问题,跨进程风险很大,故必须传receiverPermission加以限制。
问题2:广播跨进程风险怎么规避
回答2:
- exported=false
此broadcastReceiver能否接收其他App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程); - receiverPermission
- setPackage(String 路径) 指定路径下的接收器才可以接收
- 本地广播 LocalBroadcast
权限的高低设置方法如下
IntentFilter filter=new IntentFilter("com.example.dl.Broad");
filter.setPriority(100);
receiver=new Demo2Receiver();
registerReceiver(receiver,filter,"com.cn.customview.permissions.MY_BROADCAST",null);
调用IntentFilter的setPriority(int priority)方法设置优先级,参数值可以是-1000~1000,值越大,优先级越高。
同样的在静态注册中,通过设置intent-filter标签的priority属性来设置优先级,代码如下:
<receiver android:name=".MainActivity$Demo2Receiver"
android:enabled="true"
android:permission="com.cn.customview.permissions.MY_BROADCAST"
android:exported="true">// 允许跨进程接收
<intent-filter android:priority="100">// 优先级
<action android:name="com.example.dl.Broad"/>
</intent-filter>
</receiver>
截断的方法如下
class DemoReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
abortBroadcast();
}
}
有序广播优先级priority高的可以拦截优先级低的,也可以修改intent数据,对之后的接收者intent做些处理和篡改。
2.静态广播 动态广播
2.1 静态广播
如果我们设置为静态注册的时候,我们的广播接收器就一个设置为 独立外部类 或者是 静态内部类 并且在AndroidManifest.xml 中进行注册。
问:非静态内部类可以吗?
答:你在XML中声明而且使用内部类当然需要使用静态化,系统从XML实例化时只会实例化你的类,不会实例化父类,而非静态内部类需要依赖父类的实例去实例化,所以必然是实例化不了的。如果在XML中定义Receiver,你要么使用静态内部类,要么直接使用独立的类来实现。如果你的类有依赖实例,必须依赖一些实例才能正常运行,那就在程序中实例化和注册,不要使用XML来声明。
2.2 动态广播
通过代码动态注册的广播, 如:
IntentFilter filter=new IntentFilter("com.example.jie.Broad");
receiver=new Demo2Receiver();
registerReceiver(receiver,filter,"com.cn.customview.permissions.MY_BROADCAST",null);
2.3 Android 7.0 8.0 广播适配
2.3.1 Android 7.0 限制了波分静态注册广播的接收
如:1. 无法发送和监听网络变化广播 CONNECTIVITY_ACTION广播
2. 无法发送或接收ACTION_NEW_PICTURE 或ACTION_NEW_VIDEO 类型的广播。
2.3.2 Android8.0 限制了静态广播接收
8.0广播限制:
- 针对 Android 8.0的应用无法继续在其清单中为隐式广播注册广播接收器
- 应用可以继续在它们的清单中注册显式广播
- 应用可以在运行时使用Context.registerReceiver()为任意广播(不管是隐式还是显式)注册接收器
- 需要签名权限的广播不受此限制所限,因为这些广播只会发送到使用相同证书签名的应用,而不是发送到设备上的所有应用
- 问题:8.0以后,静态注册的广播接受者一定不可以接收到广播吗?
答案:不是的 8.0以后,静态注册的广播接受者是可以接收到广播的,只要广播是通过显示方式发送的,隐示意图是不可以的。 - 显示意图发送广播及 设置intent.setComponent(发送者mPackage,接受者class)
代码如下:
Intent intent = new Intent();
intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));//显示指定组件名称
sendBroadcast(intent);
3.本地广播LocalBroadcastManager
1、BroadcastReceiver用于应用之间的传递消息;
2、而LocalBroadcastManager用于应用内部传递消息,比broadcastReceiver更加高效。
3.1 LocalBroadcastManager用法
LocalBroadcastManager对象的创建
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance( this ) ;
注册广播接收器
LocalBroadcastManager.registerReceiver( broadcastReceiver , intentFilter );
发送广播
LocalBroadcastManager.sendBroadcast( intent ) ;
取消注册广播接收器
LocalBroadcastManager.unregisterReceiver( broadcastReceiver );
3.2 源码分析
链接引用自 https://www.jianshu.com/p/a2ebbafa010c
private static LocalBroadcastManager mInstance;
public static LocalBroadcastManager getInstance(Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
mHandler = new Handler(context.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_EXEC_PENDING_BROADCASTS:
executePendingBroadcasts();
break;
default:
super.handleMessage(msg);
}
}
};
}
从这个部分源码可以看出两点:
- 在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。
- 在LocalBroadcastManager构造函数中创建了一个Handler.可见LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。
- 在创建Handler的时候,用了 context.getMainLooper() ,说明这个Handler是在Android主线程中创建的,广播接收器的接收消息的时候会Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。
4.系统广播
5.广播内部的实现机制
Android广播机制可以跨进程通讯,多处得到通知的效果。所以需要由AMS(Activity Manager Service)集中管理。
还没看这块的源码,先放个博客的链接