文档章节

LocalBroadcastManager详解

哦_呢称
 哦_呢称
发布于 2017/07/09 14:33
字数 2040
阅读 3
收藏 0
点赞 0
评论 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
博文 9
码字总数 13032
作品 0
Android碎碎念 -- 广播LocalBroadcastManager的实现

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

淡定的米哥 ⋅ 2016/03/18 ⋅ 0

Fragment与Fragment、Activity通信的四种方式(三)

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

博为峰教研组 ⋅ 2016/12/05 ⋅ 0

Android 开发经验Tips(4)

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

燊在锦官城_ ⋅ 2017/04/14 ⋅ 0

如何构建一个结构清晰的Android程序

随着Android程序功能模块越来越多,模块之间的交互也日渐平常,一个结构在开始就设计良好的系统,不会因为上述的原因就出现大量的复制粘贴,如果出现大量的复制粘贴,那说明这个系统的结构设...

fneg ⋅ 2012/06/18 ⋅ 0

LocalBroadcastManager分析

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

xingjm8511 ⋅ 2016/06/06 ⋅ 0

Android:BroadcastReceiver

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

大鱼BIG_FISH ⋅ 2015/10/27 ⋅ 0

android成长日记 10.关于BroadcastReceiver的详细说明

广播看完了,然后回过头写一下,其实就是分为几种广播, 广播分为动态注册和静态注册,,,广播又分为系统广播和自定义广播, ....发送方式又分为,有序广播和无序广播....发送广播又分为直接send和利...

康康渐渐 ⋅ 2016/03/08 ⋅ 0

android学习笔记之Intent与BroadcastReceiver(三)

Local Broadcast Manager(本地广播管理者): 为了简化注册、发送广播。既然是本地,这个广播的范围相对就小了,但这样比全局的广播有效率得多。同时,这样也确保了你所发送的广播不被其它APP...

Chanimalx ⋅ 2013/01/07 ⋅ 0

Android零散技术点

Android BLE 蓝牙开发入门 逐步指导新手进行 Android ble 蓝牙的开发,避免踩坑。 Android 事件传递三部曲:本地广播 LocalBroadcastManager 我们都知道 Android 的四大组件,分别是:Activ...

掘金官方 ⋅ 2017/12/25 ⋅ 0

Android学习--09-广播

广播机制 接收广播 继承 BroadcastReceiver,重写onReceive()方法 注册监听registerReceiver(接收对象,IntentFilter) 动态注册,应用程序启动后开始接收广播 静态注册,系统启动就开始接收...

ssnoodles ⋅ 2016/09/23 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

从零开始搭建Risc-v Rocket环境---(1)

为了搭建Rocke环境,我买了一个2T的移动硬盘,安装的ubuntu-16.04 LTS版。没有java8,gcc是5.4.0 joe@joe-Inspiron-7460:~$ java -version程序 'java' 已包含在下列软件包中: * default-...

whoisliang ⋅ 8分钟前 ⋅ 0

大数据学习路线(自己制定的,从零开始学习大数据)

大数据已经火了很久了,一直想了解它学习它结果没时间,过年后终于有时间了,了解了一些资料,结合我自己的情况,初步整理了一个学习路线,有问题的希望大神指点。 学习路线 Linux(shell,高并...

董黎明 ⋅ 14分钟前 ⋅ 0

systemd编写服务

一、开机启动 对于那些支持 Systemd 的软件,安装的时候,会自动在/usr/lib/systemd/system目录添加一个配置文件。 如果你想让该软件开机启动,就执行下面的命令(以httpd.service为例)。 ...

勇敢的飞石 ⋅ 16分钟前 ⋅ 0

mysql 基本sql

CREATE TABLE `BBB_build_info` ( `community_id` varchar(50) NOT NULL COMMENT '小区ID', `layer` int(11) NOT NULL COMMENT '地址层数', `id` int(11) NOT NULL COMMENT '地址id', `full_......

zaolonglei ⋅ 25分钟前 ⋅ 0

安装chrome的vue插件

参看文档:https://www.cnblogs.com/yulingjia/p/7904138.html

xiaoge2016 ⋅ 28分钟前 ⋅ 0

用SQL命令查看Mysql数据库大小

要想知道每个数据库的大小的话,步骤如下: 1、进入information_schema 数据库(存放了其他的数据库的信息) use information_schema; 2、查询所有数据的大小: select concat(round(sum(da...

源哥L ⋅ 50分钟前 ⋅ 0

两个小实验简单介绍@Scope("prototype")

实验一 首先有如下代码(其中@RestController的作用相当于@Controller+@Responsebody,可忽略) @RestController//@Scope("prototype")public class TestController { @RequestMap...

kalnkaya ⋅ 55分钟前 ⋅ 0

php-fpm的pool&php-fpm慢执行日志&open_basedir&php-fpm进程管理

12.21 php-fpm的pool pool是PHP-fpm的资源池,如果多个站点共用一个pool,则可能造成资源池中的资源耗尽,最终访问网站时出现502。 为了解决上述问题,我们可以配置多个pool,不同的站点使用...

影夜Linux ⋅ 今天 ⋅ 0

微服务 WildFly Swarm 管理

Expose Application Metrics and Information 要公开关于我们的微服务的有用信息,我们需要做的就是将监视器模块添加到我们的pom.xml中: 这将使在管理和监视功能得到实现。从监控角度来看,...

woshixin ⋅ 今天 ⋅ 0

java连接 mongo伪集群部署遇到的坑

部署mongo伪集群 #创建mongo数据存放文件地址mkdir -p /usr/local/config1/datamkdir -p /usr/local/config2/data mkdir -p /usr/local/config3/data mkdir -p /usr/local/config1/l......

努力爬坑人 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部