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:
  1. 先说“receiverPermission”参数是干啥的。它是“没有相同权限的其他进程不可以接收到,本进程内不受影响没有此权限的也可以接受到”,当避免其他进程接收的时候可以用它。
  2. 因为有序广播存在高优先级priority拦截低priority的问题,跨进程风险很大,故必须传receiverPermission加以限制。

问题2:广播跨进程风险怎么规避
回答2:
  1. exported=false

    此broadcastReceiver能否接收其他App的发出的广播,这个属性默认值有点意思,其默认值是由receiver中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。(同样的,activity/service中的此属性默认值一样遵循此规则)同时,需要注意的是,这个值的设定是以application或者application user id为界的,而非进程为界(一个应用中可能含有多个进程);
  2. receiverPermission
  3. setPackage(String 路径) 指定路径下的接收器才可以接收
  4. 本地广播 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 限制了静态广播接收

android粘性广播的使用场景 android 粘性广播_Broadcast

8.0广播限制:

  1. 针对 Android 8.0的应用无法继续在其清单中为隐式广播注册广播接收器
  2. 应用可以继续在它们的清单中注册显式广播
  3. 应用可以在运行时使用Context.registerReceiver()为任意广播(不管是隐式还是显式)注册接收器
  4. 需要签名权限的广播不受此限制所限,因为这些广播只会发送到使用相同证书签名的应用,而不是发送到设备上的所有应用
  • 问题: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)集中管理。

还没看这块的源码,先放个博客的链接