文档章节

LocalBroadcastManager详解

哦_呢称
 哦_呢称
发布于 2017/07/09 14:33
字数 2040
阅读 3
收藏 0

本文最先发布在CSDN博客,地址:http://blog.csdn.net/hwliu51/article/details/74752250。 欢迎转载,但请标明原文地址。

###一 本地广播与全局广播区别

Android文档上LocalBroadcastManager的说明:

Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with android.content.Context#sendBroadcast: You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data. It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit. It is more efficient than sending a global broadcast through the system.

本地广播与全局广播相比较。优势:不会泄漏数据,不会因广播而出现安全漏洞,发送和接收更高效。缺点:只能在应用内部使用。

###二 主要的类和代码介绍: ####1 LocalBroadcastManager 采用单例模式,一个应用进程只有实例。如果应用为多进程,在其他非主进程调用会生成新的对象。

	//记录广播接收者和与之对应的广播过滤器集合的map
	//一个BroadcastReceiver对应多个IntentFilter
    private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
    //广播消息action和接收该Action的广播接收者集合的map
    //一个action对应多个ReceiverRecord
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();

	//记录待处理的广播消息和对应接收者的集合
    private final ArrayList<BroadcastRecord> mPendingBroadcasts
            = new ArrayList<BroadcastRecord>();

	//消息code,用于异步处理mPendingBroadcasts的消息
    static final int MSG_EXEC_PENDING_BROADCASTS = 1;
    //用于发送和在主线程处理mPendingBroadcasts
    private final Handler mHandler;

    private static final Object mLock = new Object();
    //静态实例
    private static LocalBroadcastManager mInstance;

    public static LocalBroadcastManager getInstance(Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
            	//使用的是Application的Context来创建
                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);
                }
            }
        };
    }

####2 私有的内部类ReceiverRecord 广播接收者记录类:记录广播过滤器IntentFilter和注册的BroadcastReceiver广播接收对象。

   private static class ReceiverRecord {
		//广播过滤器
        final IntentFilter filter;
        //广播接收对象
        final BroadcastReceiver receiver;
        //是否被记录到处理广播的标志,以防止重复记录。
        boolean broadcasting;

        ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
            filter = _filter;
            receiver = _receiver;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder(128);
            builder.append("Receiver{");
            builder.append(receiver);
            builder.append(" filter=");
            builder.append(filter);
            builder.append("}");
            return builder.toString();
        }
    }

3 私有的内部类ReceiverRecord

广播记录类:记录广播消息Intent和接收该广播消息的ReceiverRecord的集合。

   private static class BroadcastRecord {
		//带有广播消息的Intent
        final Intent intent;
        //接收intent的消息的广播接收者的集合
        final ArrayList<ReceiverRecord> receivers;

        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }

###三广播流程介绍

1 注册本地广播

通过静态方法getInstance获取到LocalBroadcastManager对象,然后调用registerReceiver,传入BroadcastReceiver和IntentFilter参数注册广播。

LocalBroadcastManager#registerReceiver代码:

   public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
        	//创建广播接收者记录对象,记录filter和receiver
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //获取receiver之前注册的IntentFilter对象的集合
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
            	//filters为null,说明receiver没有被注册过。
            	//创建IntenFilter集合filters,将receiver和filter记录到mReceiver中
                filters = new ArrayList<IntentFilter>(1);
                mReceivers.put(receiver, filters);
            }
            //记录filter
            filters.add(filter);
            //遍历Action,将其与接收该Action的ReceiverRecord关联
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

####2 发送本地广播 发送有两种方式,调用sendBroadcast或sendBroadcastSync方法。sendBroadcast方法不会立即处理广播,而是通过mHandler发送一个MSG_EXEC_PENDING_BROADCASTS的空消,然后在主线程异步处理。而sendBroadcastSync在调用时便处理广播,即同步处理。因此sendBroadcastSync不能在子线程中调用。

先看看LocalBroadcastManager#sendBroadcast代码:

   public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
        	//获取广播消息的基本信息
            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();

            final boolean debug = DEBUG ||
                    ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
            if (debug) Log.v(
                    TAG, "Resolving type " + type + " scheme " + scheme
                    + " of intent " + intent);

			//从mActions中获取接收该action的广播接收者记录集合
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            //如果entries不为null,则有接收该Action的广播注册。
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

				//记录符合条件的广播接收者记录对象
                ArrayList<ReceiverRecord> receivers = null;
                //------ 遍历开始 ------
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
					//broadcasting为true,则说明该receiver已被记录到receivers
                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }

					//匹配IntentFilter
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {//匹配成功
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {//创建接收者集合
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);//记录receiver
                        receiver.broadcasting = true;//标记为已记录
                    } else {
                    	//debug信息
                        if (debug) {
                            String reason;
                            switch (match) {
                                case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
                                case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
                                case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
                                case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
                                default: reason = "unknown reason"; break;
                            }
                            Log.v(TAG, "  Filter did not match: " + reason);
                        }
                    }
                }
                //------ 遍历结束 ------

				//有符合条件的广播接收者记录对象
                if (receivers != null) {
                	//重置标记
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    //封装到广播记录,并添加到待处理的集合
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    //如果没有正在处理mPendingBroadcasts,则放送异步处理消息
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    //发送成功
                    return true;
                }
            }
        }
        //发送失败
        return false;
    }

LocalBroadcastManager#sendBroadcastSync代码:

   public void sendBroadcastSync(Intent intent) {
   		//通过sendBroadcast将接收广播的receiver找出并封装为BroadcastRecord记录到
   		//mPendingBroadcasts,然后调用executePendingBroadcasts立即处理
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
    }

####3 反注册本地广播 调用unregisterReceiver注册 LocalBroadcastManager#unregisterReceiver代码:

    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
        	//将receiver从mReceiver移除,并返回相关的IntentFilter集合
            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            //遍历IntentFilter集合
            for (int i=0; i<filters.size(); i++) {
                IntentFilter filter = filters.get(i);
                //遍历Action
                for (int j=0; j<filter.countActions(); j++) {
                    String action = filter.getAction(j);
                    //获取action关联的ReceiverRecord集合
                    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--;
                            }
                        }
                        //receivers为空,则说明与action关联的ReceiverRecord已全被移除
                        //即已无ReceiverRecord与action关联,则将action从mActions删除
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

从mReceivers中删除receiver,到遍历filters和action来删除mActions中引用,从而彻底删除对receiver的引用。这样就可以防止应注册本地广播而导致的内存泄漏。

###四 回顾上述分析,再来比较普通广播与本地广播 从以上的代码和流程分析来看本地广播的优点: 1.安全:本地广播所发出的Intent只在应用内部传播,即使其他应用注册了相同的action也无法接收到广播消息。而全局广播则会传播给所有注册相同action的应用,从而导致数据泄漏。 2.高效a 本地广播大概流程 注册 LocalBroadcastManager#registerReceiver; 反注册 LocalBroadcastManager#unRegisterReceiver; 发送处理(异步) LocalBroadcastManager#sendBroadcast =》 mHandler#handleMessage =》BroadCastReceiver ; 发送处理(同步) LocalBroadcastManager#sendBroadcastSync =》BroadCastReceiver;

b 普通广播大概流程 注册 ContextImpl#registerReceiver =>IBinder => ActivityManagerService#registerReceiver 反注册 ContextImpl#unRegisterReceiver =>IBinder => ActivityManagerService#unRegisterReceiver 发送处理(只有异步) ContextImpl#sendBroadcast =>IBinder => ActivityManagerService#sendBroadcast=》ApplicationThread#scheduleReceiver =》H#handleMessage =》ActivityThread#handleReceiver =》BroadCastReceiver

本地广播的数据基本只在LocalBroadcastManager类和BroadcastReceiver内部流通。而普通广播则复杂的多。

本地广播的缺点也很明显,就是不能够跨进程传播。

###五 本地广播与EventBus 二者大概的设计思路比较相似。 LocalBroadcastManager采用了单例设计模式。注册时,使用集合容器来记录BroadcastReceiver和action。发送广播事消息时,从集合容器中查找BroadcastReceiver对象,然后将消息和BroadcastReceiver对象集合封装到BroadcastRecord对象,并加入到处理的集合中,再异步或同步处理给BroadcastReceiver。可以将mPendingBroadcasts看作为一个待处理的消息队列。

EventBus也使用了单例模式。注册时,通过反射获取需要调用的方法和参数消息以及其他配置信息,然后分别存入到相关的集合容器中。发送消息时,依据发送和配置的策略采取同步或异步处理。异步消息将加入到队列中,在Handler中遍历获取,根据消息的类型遍历获取被调用的方法然后传递消息。

© 著作权归作者所有

共有 人打赏支持
哦_呢称
粉丝 1
博文 11
码字总数 13032
作品 0
Android碎碎念 -- 广播LocalBroadcastManager的实现

在Android系统中,BroadcastReceiver的设计初衷就是从全局考虑的,可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题的。...

淡定的米哥
2016/03/18
88
0
Fragment与Fragment、Activity通信的四种方式(三)

3.使用广播 不论我们有没有用ViewPager,都可以用广播实现两个Fragment之间的通信,广播算是这里最灵活的通信方式了,我们看看在左边Fragment中发送广播: Intent intent = new Intent("show...

博为峰教研组
2016/12/05
35
0
Android 开发经验Tips(4)

每次更新一打开发小技巧O(∩_∩)O~ 1. 如何选择 compileSdkVersion, minSdkVersion 和 targetSdkVersion 理想上,在稳定状态下三者的关系应该更像这样: 用较低的 minSdkVersion 来覆盖最大的...

燊在锦官城_
2017/04/14
0
0
LocalBroadcastManager分析

在Android系统中,BroadcastReceiver的设计初衷就是从全局考虑的,可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题的,...

xingjm8511
2016/06/06
67
0
Android:BroadcastReceiver

Broadcast分类 注册方式: 动态广播 在代码中注册receiver 一定要手动在onDestroy()时调用unregisterReveiver()来取消注册 静态广播 在AndroidManifest.xml中注册receiver(主要是设定action)...

大鱼BIG_FISH
2015/10/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

阿里开源的 java 诊断工具—— Arthas

Arthas 是 阿里巴巴最近开源出来的一个针对 java 的工具,主要是针对 java 的问题进行诊断! 一、概述 这个工具可以协助你做下面这些事情: 这个类是从哪个 jar 包加载而来的? 为什么会报各...

xiaomin0322
27分钟前
2
0
去除shell read 读取的最后一个字符

# 读取管道数据cat | while read line; do echo $line # 此时 line包含 \n or \r\ndone# 去除 read 读取的特殊字符line=${line%?} # 去除最后一个字符...

tigerBin
28分钟前
1
0
Qt之listView设置编辑状态

QListView默认是可以编辑的,可以用setEditTrigers设置QListView的条目是否可以编辑,以及如何进入编辑状态。比如: ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | QAb...

OceanStar
28分钟前
1
0
Linux批量替换

sed -i "s/http://cache.co188.com///image.co188.com/g" grep http:\/\/image.co188.com -rl . *.html sed -i "s/http://cache.co188.com///cache.co188.com/g" grep http:\/\/cache.co188.......

cpaku
38分钟前
1
0
设置plsql永久注册码

填写注册码: Product Code:4t46t6vydkvsxekkvf3fjnpzy5wbuhphqz serial Number:601769 password:xs374ca...

小橙子的曼曼
43分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部