Android编程之LocalBroadcastManager源码详解

原创 2014年02月07日 22:55:15

LocalBroadcastManager 是V4包中的一个类,主要负责程序内部广播的注册与发送。也就是说,它只是适用代码中注册发送广播,对于在AndroidManifest中注册的广播接收,则不适用。

官方英文解释如下:

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 sendBroadcast(Intent):

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. 


接下来如正题,先看一下全局变量:

private final Context mAppContext;
private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap();

private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap();

private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList();
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
private static LocalBroadcastManager mInstance;

mAppContext:即ApplicationContext,所以不用担心内存泄漏问题。

mReceivers:记录注册的BroadcastReceiver及其IntentFilter的数组,这里为什么是数组,下面会有讲到。

mActions:记录IntentFilter中的action对应的BroadcastReceiver数组。虽然这里写的是ReceiverRecord类型,但它实际上是一个内部类,主要保存了BroadcastReceiver及其对应的IntentFilter。

mPendingBroadcasts:在发送广播时,会根据Intent的action,找到与之相对应的BroadcastReceiver。还记得吗?action是可以对应多个BroadcastReceiver,所以这里是数组。

mHandler:就做了一件事情,发送广播。

mLock:同步锁。

mInstance:自己本身的实例对象,有经验的可能已经猜到了,没错,LocalBroadcastManager是一个单例。


看一下它的构造方法,标准的单例写法:

public static LocalBroadcastManager getInstance(Context context) {
	synchronized (mLock) {
		if (mInstance == null) {
			mInstance = new LocalBroadcastManager(
					context.getApplicationContext());
		}
		return mInstance;
	}
}

private LocalBroadcastManager(Context context) {
	this.mAppContext = context;
	this.mHandler = new Handler(context.getMainLooper()) {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_EXEC_PENDING_BROADCASTS:
				LocalBroadcastManager.this.executePendingBroadcasts();
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
}

上面谈到的mAppContext是ApplicationContext的证据:mInstance = new LocalBroadcastManager(context.getApplicationContext());

而mHandler就是在构造时创建的,内部就做了一件事,发送广播:executePendingBroadcasts。


接下来就看一下如何注册广播接收者:

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
	synchronized (this.mReceivers) {
		ReceiverRecord entry = new ReceiverRecord(filter, receiver);
		ArrayList filters = (ArrayList) this.mReceivers.get(receiver);
		if (filters == null) {
			filters = new ArrayList(1);
			this.mReceivers.put(receiver, filters);
		}
		filters.add(filter);
		for (int i = 0; i < filter.countActions(); i++) {
			String action = filter.getAction(i);
			ArrayList entries = (ArrayList) this.mActions.get(action);
			if (entries == null) {
				entries = new ArrayList(1);
				this.mActions.put(action, entries);
			}
			entries.add(entry);
		}
	}
}

就是将BroadcastReceiver和IntentFilter建立一对多的对应关系。可以通过BroadcastReceiver找到其对应的IntentFilter,也可以通过IntentFilter中的action找到所对应的BroadcastReceiver。这里还要解释一下上面提到的mReceivers中,为什么保存的IntentFilter是数组形式。我们知道,IntentFilter中是可以保存多个action的,这也就是为什么它初始化成1个长度的数组。那么这里的数组的意义,其实很简单,就是能支持传入多个IntentFilter。虽然是支持传入多个IntentFilter,但如果里面的action是同名的话,也还是按同一个处理的,后面代码就能看出,action是以键的方法存起来的。


有注册,就得有取消注册:

public void unregisterReceiver(BroadcastReceiver receiver) {
	synchronized (this.mReceivers) {
		ArrayList filters = (ArrayList) this.mReceivers.remove(receiver);
		if (filters == null) {
			return;
		}
		for (int i = 0; i < filters.size(); i++) {
			IntentFilter filter = (IntentFilter) filters.get(i);
			for (int j = 0; j < filter.countActions(); j++) {
				String action = filter.getAction(j);
				ArrayList receivers = (ArrayList) this.mActions.get(action);
				if (receivers != null) {
					for (int k = 0; k < receivers.size(); k++) {
						if (((ReceiverRecord) receivers.get(k)).receiver == receiver) {
							receivers.remove(k);
							k--;
						}
					}
					if (receivers.size() <= 0)
					this.mActions.remove(action);
				}
			}
		}
	}
}

简单来说,就是将BroadcastReceiver从mReceivers和mActions中移除掉。由于BroadcastReceiver是mReceivers的键,所以移除掉比较简单。而mActions就稍微复杂一些,需要根据BroadcastReceiver中的IntentFilter数组,从mActions中移除掉。

还记得注册时,同名的action可以对应不同的BroadcastReceiver吗,注意这里的一句话:if (((ReceiverRecord) receivers.get(k)).receiver == receiver),没错,它只会移除掉当前的,不会将action对应的BroadcastReceiver都删除掉。


最后就是关键的    public boolean sendBroadcast(Intent intent)方法,整个方法都是synchronized (this.mReceivers)的,由于这个方法内容太长,这里分段来讲解:

String action = intent.getAction();
String type = intent.resolveTypeIfNeeded(this.mAppContext.getContentResolver());

Uri data = intent.getData();
String scheme = intent.getScheme();
Set categories = intent.getCategories();

boolean debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION ) != 0;

首先取出intent中的参数,除了action外,其他都是用来作比较的。如果在intent中setFlag设置了Intent.FLAG_DEBUG_LOG_RESOLUTION,就会可以输出一些log信息。


然后就是从mActions中取出intent的action所对应的ReceiverRecord

ArrayList entries = (ArrayList) this.mActions.get(intent.getAction());
										
ArrayList receivers = null;
for (int i = 0; i < entries.size(); i++)

这段就是for循环里面的内容:

ReceiverRecord receiver = (ReceiverRecord) entries.get(i);

if (receiver.broadcasting) {
					
	} else {
		int match = receiver.filter.match(action, type, scheme,
		data, categories, "LocalBroadcastManager");

		if (match >= 0) {						
			if (receivers == null) {
				receivers = new ArrayList();
			}
			receivers.add(receiver);
			receiver.broadcasting = true;
		} else {
												
		}
	}
}

在这里,如果是发送中,就什么也不做。否则,先匹配一下receiver中的IntentFilter,如果匹配上,就设置其为发送中,即设置变量broadcasting为true。其意义在于避免重复发送。


最后,添加到ArrayList中:this.mPendingBroadcasts.add(new BroadcastRecord(intent,receivers));通知Handler,执行executePendingBroadcasts()方法。

if (receivers != null) {
	for (int i = 0; i < receivers.size(); i++) {
		((ReceiverRecord) receivers.get(i)).broadcasting = false;
	}
	this.mPendingBroadcasts.add(new BroadcastRecord(intent,
			receivers));
	if (!this.mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
		this.mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
	}
	return true;
}

executePendingBroadcasts()方法就很简单了,就是取出mPendingBroadcasts数组中的BroadcastReceiver(在ReceiverRecord中保存其对象),调用其onReceive方法。

private void executePendingBroadcasts() {
	while (true) {
		BroadcastRecord[] brs = null;
		synchronized (this.mReceivers) {
			int N = this.mPendingBroadcasts.size();
			if (N <= 0) {
				return;
			}
			brs = new BroadcastRecord[N];
			this.mPendingBroadcasts.toArray(brs);
			this.mPendingBroadcasts.clear();
		}
		for (int i = 0; i < brs.length; i++) {
			BroadcastRecord br = brs[i];
			for (int j = 0; j < br.receivers.size(); j++)
				((ReceiverRecord) br.receivers.get(j)).receiver.onReceive(
						this.mAppContext, br.intent);
		}
	}
}

还有一个方法sendBroadcastSync,平常我们一般不会用到这个方法,这里放一下源码:

public void sendBroadcastSync(Intent intent) {
	if (sendBroadcast(intent))
		executePendingBroadcasts();
}

这里再补一个其内部类ReceiverRecord的源码:

private static class ReceiverRecord {
	final IntentFilter filter;
	final BroadcastReceiver receiver;
	boolean broadcasting;

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

小结:

1、LocalBroadcastManager在创建单例传参时,不用纠结context是取activity的还是Application的,它自己会取到tApplicationContext。

2、LocalBroadcastManager只适用于代码间的,因为它就是保存接口BroadcastReceiver的对象,然后直接调用其onReceive方法。

3、LocalBroadcastManager注册广播后,当该其Activity或者Fragment不需要监听时,记得要取消注册,注意一点:注册与取消注册在activity或者fragment的生命周期中要保持一致,例如onResume,onPause。

4、LocalBroadcastManager虽然支持对同一个BroadcastReceiver可以注册多个IntentFilter,但还是应该将所需要的action都放进一个IntentFilter,即只注册一个IntentFilter,这只是我个人的建议。

5、LocalBroadcastManager所发送的广播action,只能与注册到LocalBroadcastManager中BroadcastReceiver产生互动。如果你遇到了通过LocalBroadcastManager发送的广播,对面的BroadcastReceiver没响应,很可能就是这个原因造成的。

LocalBroadcastManager局部广播管理器

LocalBroadcastManager 局部广播管理器,包含在v4包中,因为局部广播的作用域小,所以使用LocalBroadcastManager比发送全局广播更加高效。而且使用LocalBr...
  • Jasonez
  • Jasonez
  • 2015年06月24日 00:11
  • 610

Android 三种sendBroadCast的方式对比

作为android 的四大基本组件之一的 BroadCast Receiver,是进行进程间通信的重要手段,几乎所有的应用都会注册和发送各种不同的intent, 那么有一个问题,你是否了解Intent...
  • sduliulun
  • sduliulun
  • 2015年12月07日 16:10
  • 15346

LocalBroadcastManager详解

LocalBroadcastManager代码分析,注册Receiver ,以及发送和接收广播流程分析。本地广播与普通广播比较,以及与EventBus设计架构比较。...
  • hwliu51
  • hwliu51
  • 2017年07月07日 17:56
  • 301

Android中LocalBroadcastManager的基本用法及源码分析

本篇博客主要记录一下LocalBroadcastManager的基本用法, 同时分析一下LocalBroadcastManager的源码,看看其功能实现的原理。...
  • Gaugamela
  • Gaugamela
  • 2017年02月23日 17:06
  • 1451

Android编程之LocalBroadcastManager源码详解

LocalBroadcastManager 是V4包中的一个类,主要负责程序内部广播的注册与发送。也就是说,它只是适用代码中注册发送广播,对于在AndroidManifest中注册的广播接收,则不适用...
  • xyz_fly
  • xyz_fly
  • 2014年02月07日 22:55
  • 7390

Android Broadcast广播总结

Android Broadcast广播总结
  • qq402164452
  • qq402164452
  • 2016年11月02日 15:32
  • 382

编程之禅 浅谈封装

作为一个整天与代码打交道的人,你真的会coding吗? 今天依旧来反思一下自身。伊始大一的时候,刚接触到了C语言,一门神奇的语言。老师就教导我们要多敲例子,照着书本敲就可以了。可能当时并没有真正的理解...
  • Marksinoberg
  • Marksinoberg
  • 2016年06月17日 10:00
  • 6835

JDBC编程之程序优化

首先,新建包package com.djx.entity;,其中类为 Identity ,实现代码如下: package com.djx.entity; public abstract class...
  • dengjiaxing0321
  • dengjiaxing0321
  • 2016年03月16日 15:49
  • 129

Android 之使用LocalBroadcastManager解决BroadcastReceiver安全问题

参考博客: http://blog.csdn.net/t12x3456/article/details/9256609 http://blog.csdn.net/lihenair/arti...
  • lei19880402
  • lei19880402
  • 2015年07月02日 13:38
  • 356

使用BroadCastReceiver

概念:BroadCast是一种广泛运用的,在应用程序之间传输信息的机制,Android中的广播与传统意义上的电台广播类似,一个广播可以有任意个接收者。广播机制是一个典型的发布-订阅模式。 Andro...
  • a992036795
  • a992036795
  • 2016年06月07日 08:50
  • 612
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android编程之LocalBroadcastManager源码详解
举报原因:
原因补充:

(最多只允许输入30个字)