Android中Broadcast Receiver知识点总结

定义:

在Android中,Broadcast是一种广泛应用在应用程序之间传输信息的机制,Android中我们要发送的内容是一个Intent,而我们要发送的数据由Intent包装。
而BroadcastReceiver是一个专注于接收广播发出的信息,并作出对应处理的组件。BroadcastReceiver没有用户界面,但是它可以启动Activity来相应接收到的消息,或者使用NotificationManager发出通知来提醒用户

广播类似于Java当中的观察者模式,即被观察者数据发生变化后,给观察者发出通知做相应的数据处理。

使用场景:

  • 同一个APP具有多个进程的不同组件之间的消息通信

比如获取定位数据或者天气数据的Service放在了一个进程,这时候可以通过广播来与主进程进行数据交换达到更新UI等功能

  • 不同APP之间的组件进行消息通信

比如系统应用发出来电信息,短信信息,电源信息等。或者一些大公司有多个APP装在用户手机上,这样不同应用之间就可以通过广播来进行通信

广播分类:

  • Normal Boradcast:即普通广播,可以通过Conext.sendBroadcast(intent)、Conext.sendBroadcast(intent,receiverPermission)等发送广播
  • System Broadcast:即系统广播,系统广播在系统内部当特定事件发生时,由系统自动发出,开发者可以接受这些广播;如:开机启动,网络状态改变,拍照,屏幕关闭与开启等特定事件
  • Ordered Broadcast:即有序广播,其实有序广播可以算做是系统广播;有序广播中的“有序”是针对广播接收者而言的,指的是发送出去的广播被BroadcastReceiver按照先后循序接收。有序广播的定义过程与普通广播无异,只是其主要发送方式变为:sendOrderedBroadcast(intent, receiverPermission, …);有序广播特点如下:
  • 多个BroadcastReceiver接收有序广播时,是按照先后顺序接收的,先后顺序判定标准遵循为:将当前系统中所有有效的动态注册和静态注册的BroadcastReceiver按照priority属性值从大到小排序;对于具有相同的priority的动态广播和静态广播,动态广播会排在前面。
  • 先接收的BroadcastReceiver可以对此有序广播进行截断,使后面的BroadcastReceiver不再接收到此广播,也可以对广播进行修改,使后面的BroadcastReceiver接收到广播后解析得到错误的参数值。当然,一般情况下,不建议对有序广播进行此类操作,尤其是针对系统中的有序广播。
  • Local Broadcast:即本地广播,App应用内广播,广播的发送与接收只在当前应用内发生

广播注册:

  • 静态注册:在manifest.xml中注册,注册完成就一直运行,也叫常驻型广播,在最新API里,默认情况下如果APP所在进程以及结束,静态广播也不能接收广播了
  • 动态注册:通过registerReceiver方法在代码里注册,它的生命周期跟随注册它的Activity的生命周期;在Activity的onDestory方法中必须使用unregisterReceiver方法解除注册,否则造成内存泄漏;也叫非常驻型广播

这里写图片描述

广播实现机制:

  1. 自定义广播接受者BroadcastReceiver,重写onReceive()方法
  2. 广播接受者通过Binder机制向AMS(Activity Manager Service)进行注册
  3. 广播发送者通过Binder向AMS发送广播
  4. AMS查找符合相应条件(Intent-filter或者permission)的BroadcastReceiver,将广播发送到BroadcastReceiver相应的消息队列中(通常是Activity的消息队列,即两者使用相同的Looper)
  5. 消息循环执行拿到此广播,回调BroadcastReceiver的onReceive方法

本地广播:

我们知道BroadcastReceiver设计的初衷就是从全局考虑,方便应用程序和系统程序之间、应用程序与应用程序之间、应用程序内部的通信;但是对于单个应用程序使用BroadcastReceiver是存在安全风险的:
如果别人通过反编译你的程序,获取到你的广播接收者的intent-filter或者permission,那就可以:

  1. 其它应用可以针对性的发出与当前应用intent-filter相匹配的广播,由此导致当前应用接收错误的数据,比如发送一个链接用来植入广告等
  2. 其它应用可以注册与当前应用一致的intent-filter,获取相关数据

解决方法如下:

  1. 对于同一App内部发送和接收广播,将exported属性设置成false,使得非本App内部发出的广播不被接收
  2. 在广播发送和接收时,都增加上相应的permission,用于权限验证
  3. 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中
  4. 使用LocalBroadcastManager,即本地广播,本地广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App
使用本地广播优势:
  1. 使用它发送的广播只在APP内部传播,不必担心泄露数据
  2. 其它APP无法对你的APP发送伪造广播,不用担心有安全漏洞被利用
  3. 比通过系统发送全局广播更有效。

本地广播源码解析:

LocalBroadcastManager的构造方法
    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);
                }
            }
        };
    }

从这些代码可以总结到:

  1. LocalBroadcastManager使用了单例模式,并且把外部传入的Context转化成了ApplicationContext,避免造成内存泄漏
  2. 在构造方法中创建了一个Handler,可见 LocalBroadcastManager的本质上是通过Handler机制发送和接收消息的。
  3. 在创建Handler的时候,使用了context.getMainLooper(),说明这个Handler是在UI线程创建的,即广播接收者是在主线程接收消息,所以开发者不能在onReceive方法中做耗时操作
LocalBroadcastManager的数据结构
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();
private final ArrayList<BroadcastRecord> mPendingBroadcasts
            = new ArrayList<BroadcastRecord>();
  1. mReceivers是一个Map,key是开发者注册的广播接收者,value是每个BroadcastReceiver的多个IntentFileter组成的List
  2. mActions也是一个Map,key是IntentFilter中的action,value是ReceiverRecord组成的List;ReceiverRecord是一个内部类,维护了IntentFilter和BroadcastReceiver,记录了广播接收者,可以理解为 广播接收器记录者
  3. mPendingBroadcasts是一个List,保存了BroadcastRecord,这也是一个内部类,里面维护了Intent和ReceiverRecord的List集合;也就是把广播发送者发送的数据和对应的广播接收器封装成BroadcastRecord;可以理解为 广播待发送记录者
广播注册:
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            //构建广播接收器记录者,包含过滤条件和广播接收器
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //构建保存过滤条件的集合
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<IntentFilter>(1);
                //将每个广播接收器和对应的过滤条件进行映射
                mReceivers.put(receiver, filters);
            }
            //保存过滤条件
            filters.add(filter);

            for (int i=0; i<filter.countActions(); i++) {
                //将每个过滤条件对象的action取出
                String action = filter.getAction(i);
                //构建保存 广播接收器记录者 的集合
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    //将ArrayList容量设置为1,节约内存,不过容量还是会随着元素增加自增长
                    entries = new ArrayList<ReceiverRecord>(1);
                    //将action和广播接收器记录者 进行映射
                    mActions.put(action, entries);
                }
                //保存 广播接收器记录者
                //注意这里entries会添加重复的entry
                entries.add(entry);
            }
        }
    }

这个注册方法主要就是将广播接收者和过滤条件保存在mReceivers和mActions中

广播解除注册
public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            //从mReceivers集合移除该广播接收器,并取出对应的过滤条件集合
            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=0; i<filters.size(); i++) {
                //取出过滤条件
                IntentFilter filter = filters.get(i);
                for (int j=0; j<filter.countActions(); j++) {
                    //取出每个过滤条件对应的action
                    String action = filter.getAction(j);
                    //从mActions取出每个action对应的 广播接收器记录者 集合,然后将集合清空
                    ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=0; k<receivers.size(); k++) {
                            //将从广播接收器记录者中取出的广播接收器与当前要
                            //解除注册的广播接收器进行比较,如果相同,就移除
                            if (receivers.get(k).receiver == receiver) {
                                receivers.remove(k);
                                k--;
                            }
                        }
                        if (receivers.size() <= 0) {
                            //从mActions中移除与当前广播接收器对应的action符合的广播接收器记录者 集合
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

解除注册也就是把之前保存的数据清空

发送广播
public boolean sendBroadcast(Intent intent) {

        synchronized (mReceivers) {
            //从intent中取出action
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            //从mActions集合中通过Intent中的action取出 广播接收器记录者 集合
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());

            if (entries != null) {
                //保存要处理该Intent的广播接收器记录者
                ArrayList<ReceiverRecord> receivers = null;

                for (int i=0; i<entries.size(); i++) {
                    //取出每个 广播接收器记录者
                    ReceiverRecord receiver = entries.get(i);

                    //如果已经保存过了就跳过,因为注册方法中提到过entries可能会添加相同的ReceiverRecord
                    if (receiver.broadcasting) {
                        continue;
                    }

                    //将广播接收器记录者中维护的过滤条件与当前Intent进行比较
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");

                    if (match >= 0) {
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        //如果能匹配到就将该 广播接收器记录者 保存到新的集合
                        receivers.add(receiver);
                        //表示 该广播接收器记录者 已经添加过了
                        receiver.broadcasting = true;
                    } 
                }

                if (receivers != null) {
                    //将新的集合遍历,把broadcasting置否,以便于下次开发者继续发送广播
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    //将广播待发送记录者保存在mPendingBroadcasts
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    //判断是否在处理广播消息,没有就发送消息,让handler处理
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

发送广播逻辑:

  1. 过滤:将开发者传进来的Intent与已经保存注册的BroadcastReceiver的IntentFilter进行匹配
  2. 保存:将筛选出来的Intent和与其符合的BroadcastReceiver保存到mPendingBroadcasts集合中
  3. 发送消息让Handler去处理
将发送的Intent回调给广播接收器
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);
                }
            }
        };
    }

private void executePendingBroadcasts() {
        while (true) {
            BroadcastRecord[] brs = null;
            synchronized (mReceivers) {
                //没有需要处理的Intent,就return
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                //实例化BroadcastRecord数组,并且将mPendingBroadcasts中的数据添加到其中
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i<brs.length; i++) {
                //取出每个广播待发送记录者
                BroadcastRecord br = brs[i];
                for (int j=0; j<br.receivers.size(); j++) {
                    //取出 广播待发送记录者 中维护的 广播接收器记录者 集合
                    //再取出 广播接收器记录者 集合中的广播接收器
                    //再回调广播接收器的onReceive方法
                    br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
                }
            }
        }
}

主要是通过构造方法中实例化的Handler来处理消息;每发送一次广播,就通过Handler发送一次消息处理,将Intent回调给对应的BroadcastRecevier的onReceive处理

本地广播总结:

  • LocalBroadcastManager高效的原因主要是因为它内部是通过Handler实现的,它的sendBroadcast发送广播与其它广播发送不一样,是使用Handler发送Message实现
  • 隔离其它APP广播的原因是因为本地广播是由Hnadler实现,不同应用(即不同进程)之间的内存块是相隔离的,不能直接访问,所以别的应用无法向我们的应用发送广播,我们发送的广播别的应用也不会收到
  • LocalBroadcastManager通过mReceivers集合将BroadcastRecevier与自己的IntentFilter做映射进行保存;通过mActions集合将action和封装了IntentFilter与BroadcastReceiver的ReceiverRecord做映射进行保存;通过mPendingBroadcasts集合保存BroadcastRecord,也就是把Intent和对应的BroadcastReceiver保存起来;最后在Handler里取出mPendingBroadcasts中的BroadcastReceiver,通过它的onReceive方法把Intent回调给它
  • BroadcastReceiver的通信是基于Binder机制,而LocalBroadcastManager的核心是基于Handler机制

本地广播与非本地广播发送使用

本地广播

LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(this);
BroadcastDemo broadcastDemo = new BroadcastDemo();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com,mangoer.boradcast");
broadcastManager.registerReceiver(broadcastDemo,intentFilter);

broadcastManager.unregisterReceiver(broadcastDemo);

非本地广播

BroadcastDemo broadcastDemo = new BroadcastDemo();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com,mangoer.boradcast");
Context.registerReceiver(broadcastDemo,intentFilter);

Context.unregisterReceiver(broadcastDemo);

BroadcastReceiver接收顺序

  • 使用Context.sendBroadcast发送,如果依次注册了三个广播接收者A/B/C,那么接受顺序是C/B/A,即后注册的总是先收到广播
  • 使用LocalBroadcastManager.sendBroadcast发送,如果依次注册了三个广播接收者A/B/C,那么接受顺序是A/B/C,即先注册的总是先收到广播

猜你喜欢

转载自blog.csdn.net/qq_30993595/article/details/81180996