以前有被问到过Android普通广播和本地广播的区别,因此打算分析下本地广播的实现原理以及简单结束二者的区别,算是对自个人一此源码学习总结。java
目前官网文档上是说LocalBroadcastManager
被废弃了,若是想使用的话须要自行依赖或者使用LiveData
(后面会简单介绍下其用法)来代替它。android
首先咱们在项目添加本地广播的依赖设计模式
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
复制代码
界面是Activity
包裹两个Fragment
,一个负责接受广播刷新界面,另外一个负责发送广播。数组
布局比较简单,贴个大概就晓得了。安全
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".activity.localbroad.LocalBroadActivity">
<fragment android:id="@+id/fragmentOne" android:name="com.vico.livedatademo.fragment.localbroad.LocalBroadOneFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" tools:layout="@layout/fragment_local_broad_one" />
<View android:layout_width="match_parent" android:layout_height="3dp" android:background="@android:color/black" />
<fragment android:id="@+id/fragmentTwo" android:name="com.vico.livedatademo.fragment.localbroad.LocalBroadTwoFragment" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" tools:layout="@layout/fragment_local_broad_two" />
</LinearLayout>
复制代码
从上图能够看到,除本地广播以外我还增长了普通广播发送的功能,目的也是为了下文的跨应用接收作验证。bash
来看看代码的实现部分:app
class LocalBroadOneFragment : Fragment() {
companion object {
const val LOCAL = "com.vico.livedatademo_local"
const val EXPORT = "com.vico.livedatademo_export"
}
private lateinit var localBroadcast: LocalBroadcast
private lateinit var exportBroadcast: ExportBroadcast
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
return inflater.inflate(R.layout.fragment_local_broad_one, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//注册本地广播
localBroadcast = LocalBroadcast()
//指定action
val localIntentFilter = IntentFilter(LOCAL)
LocalBroadcastManager.getInstance(requireContext()).registerReceiver(localBroadcast, localIntentFilter)
//注册普通广播
exportBroadcast = ExportBroadcast()
val exportIntentFilter = IntentFilter(EXPORT)
requireContext().registerReceiver(exportBroadcast, exportIntentFilter)
}
override fun onDetach() {
//别忘了最后要进行注销的操做
LocalBroadcastManager.getInstance(requireContext()).unregisterReceiver(localBroadcast)
requireContext().unregisterReceiver(exportBroadcast)
super.onDetach()
}
//自定义广播,用来接收本地广播发来的数据
inner class LocalBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == LOCAL) {
tv1.text = intent.getStringExtra(LocalBroadTwoFragment.LOCAL_EVENT) ?: ""
}
}
}
inner class ExportBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == EXPORT) {
tv2.text = intent.getStringExtra(LocalBroadTwoFragment.EXPORT_EVENT) ?: ""
}
}
}
}
复制代码
class LocalBroadTwoFragment : Fragment() {
companion object {
const val LOCAL_EVENT = "local_event"
const val EXPORT_EVENT = "export_event"
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_local_broad_two, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//发送一条包含时间戳内容的本地广播,指定action
btnLocal.setOnClickListener {
val localIntent = Intent(LocalBroadOneFragment.LOCAL)
localIntent.putExtra(LOCAL_EVENT, System.currentTimeMillis().toString())
LocalBroadcastManager.getInstance(requireContext()).sendBroadcast(localIntent)
}
//发送一条包含时间戳内容的普通广播,指定action
btnNormal.setOnClickListener {
val exportIntent = Intent(LocalBroadOneFragment.EXPORT)
exportIntent.putExtra(EXPORT_EVENT, (System.currentTimeMillis() / 100).toString())
requireContext().sendBroadcast(exportIntent)
}
}
}
复制代码
来看下运行效果:dom
em........这样好像也没什么区别嘛,没事咱们在新启一个应用来看看可否接收当前应用发出的广播。ide
新建的Phone Module代码跟上面接收广播的相似函数
OtherActivity:
class OtherActivity : AppCompatActivity() {
companion object {
const val LOCAL = "com.vico.livedatademo_local"
const val EXPORT = "com.vico.livedatademo_export"
}
private lateinit var localBroadcast: LocalBroadcast
private lateinit var exportBroadcast: ExportBroadcast
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_other)
localBroadcast = LocalBroadcast()
val localIntentFilter = IntentFilter(LOCAL)
LocalBroadcastManager.getInstance(this).registerReceiver(localBroadcast, localIntentFilter)
exportBroadcast = ExportBroadcast()
val exportIntentFilter = IntentFilter(EXPORT)
registerReceiver(exportBroadcast, exportIntentFilter)
}
override fun onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(localBroadcast)
unregisterReceiver(exportBroadcast)
super.onDestroy()
}
inner class LocalBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == LOCAL) {
tv1.text = intent.getStringExtra("local_event") ?: ""
}
}
}
inner class ExportBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
context ?: return
intent ?: return
if (intent.action == EXPORT) {
tv2.text = intent.getStringExtra("export_event") ?: ""
}
}
}
}
复制代码
activity_other.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" tools:context=".OtherActivity">
<TextView android:id="@+id/tv1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="来自本地广播的值" />
<TextView android:id="@+id/tv2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="来自普通广播的值" />
</LinearLayout>
复制代码
运行一下,看下这个新建的应用可否接收到另外一个应用发送出来的广播。
确实本地广播没法作到跨应用发送,那么其内部的实现原理是什么呢?下面咱们一块儿来看看
经过对LocalBroadcastManager
的使用咱们能够得知,LocalBroadcastManger
采用了单例设计模式,将其构造函数私有化。看下构造函数里作了些上什么:
private final Context mAppContext;
static final int MSG_EXEC_PENDING_BROADCASTS = 1;
private final Handler mHandler;
private static final Object mLock = new Object();
private static LocalBroadcastManager mInstance;
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
synchronized (mLock) {
if (mInstance == null) {
mInstance = new LocalBroadcastManager(context.getApplicationContext());
}
return mInstance;
}
}
private LocalBroadcastManager(Context context) {
mAppContext = context;
//初始化一个在主线程运行的Handler
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);
}
}
};
}
复制代码
嗯,在构造函数里初始化了一个在主线程运行的Handler,在接收到消息后就会执行待处理的广播。
好的,接下去看看注册registerReceiver
函数里作了什么:
//维护不一样BroadcastReceiver的ReceiverRecord集合的HashMap
private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
= new HashMap<>();
//维护不一样Action的ReceiverRecord集合的HashMap
private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
private static final class ReceiverRecord {
final IntentFilter filter;
final BroadcastReceiver receiver;
boolean broadcasting;
//是否被注销
boolean dead;
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);
if (dead) {
builder.append(" DEAD");
}
builder.append("}");
return builder.toString();
}
}
/**
* 注册一个接收匹配给定的IntentFilter任何本地广播
*
* @param receiver 处理广播的BroadCastReceiver
* @param filter
*/
public void registerReceiver(@NonNull BroadcastReceiver receiver,
@NonNull IntentFilter filter) {
synchronized (mReceivers) {
//将filter和receiver包装成ReceiverRecord对象
ReceiverRecord entry = new ReceiverRecord(filter, receiver);
//查找mReceivers这个HashMap里是否有对应的key,有则在value里添加一条记录,无则put
ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
if (filters == null) {
filters = new ArrayList<>(1);
mReceivers.put(receiver, filters);
}
filters.add(entry);
//mActions一样也是这个道理
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);
}
}
}
复制代码
能够看出registerReceiver
函数是对两个HashMap的新增操做,那么unregisterReceiver
同理是对两个HashMap的删除操做:
public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
synchronized (mReceivers) {
final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
if (filters == null) {
return;
}
for (int i=filters.size()-1; i>=0; i--) {
final ReceiverRecord filter = filters.get(i);
//将ReceiverRecord标记为已注销
filter.dead = true;
for (int j=0; j<filter.filter.countActions(); j++) {
final String action = filter.filter.getAction(j);
final ArrayList<ReceiverRecord> receivers = mActions.get(action);
if (receivers != null) {
for (int k=receivers.size()-1; k>=0; k--) {
final ReceiverRecord rec = receivers.get(k);
if (rec.receiver == receiver) {
//将ReceiverRecord标记为已注销
rec.dead = true;
receivers.remove(k);
}
}
if (receivers.size() <= 0) {
mActions.remove(action);
}
}
}
}
}
}
复制代码
好的,看完注册和注销,咱们看看发送广播sendBroadcast(Intent intent)
是怎么实现的:
private static final String TAG = "LocalBroadcastManager";
private static final boolean DEBUG = false;
private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
private static final class BroadcastRecord {
final Intent intent;
final ArrayList<ReceiverRecord> receivers;
BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
intent = _intent;
receivers = _receivers;
}
}
public boolean sendBroadcast(@NonNull 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);
//查询是否有action匹配的记录,无则结束
ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
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);
//跳过正在执行的记录
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.broadcasting = true;
} else {
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));
//Handler发送一条消息,最终在handleMessage里会执行处理广播的操做executePendingBroadcasts
if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
}
return true;
}
}
}
return false;
}
复制代码
最后看看executePendingBroadcasts
:
void executePendingBroadcasts() {
while (true) {
final BroadcastRecord[] brs;
synchronized (mReceivers) {
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i=0; i<brs.length; i++) {
final BroadcastRecord br = brs[i];
final int nbr = br.receivers.size();
for (int j=0; j<nbr; j++) {
final ReceiverRecord rec = br.receivers.get(j);
if (!rec.dead) {
rec.receiver.onReceive(mAppContext, br.intent);
}
}
}
}
}
复制代码
将mPendingBroadcasts里元素进行复制并清空,复制后的数组遍历执行receiver
。本地广播的流程到这里就结束了,下面讲讲它和普通的广播有什么区别。
两个不一样的应用在Android系统中至关于两个不一样的进程。当前应用发出的普通广播可以被其余应用所接收到,那么这势必是一个进程间通讯的过程(IPC
)。
广播的注册分为静态和动态两种注册方式,其中静态注册的广播在应用安装时由系统自动完成注册,具体点是PMS(PackageMangerService)完成的。这里就只简单分析下广播的动态注册过程。动态注册的过程从ContextWrapper
的registerReceiver
方法开始。ContextWrapper
并无作什么实际工做,将过程交给了ContextImpl
来完成。
@Override
public Intent registerReceiver( BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
复制代码
ContextImpl
的registerReceiver
方法调用了本身的registerReceiverInterval
方法,实现以下:
private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();
}
}
try {
final Intent intent = ActivityManager.getService().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
broadcastPermission, userId, flags);
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
复制代码
系统首先从mPackageInfo
获取IIntentReceiver
对象,而后再采用跨进程的方式向AMS发送广播注册的请求。IIntentReceiver
是一个Binder
接口,它的具体实现是LoadedApk.ReceiverDispatcher.InnerRecevier
,ReceiverDispatcher
的内部同时保存了BroadcastReceiver
和InnerReceiver
,这样当接收到广播时,ReceiverDispatcher
能够很方便地调用BroadcastReceiver
的onReceive
方法。
相比而言,使用LocalBroadcastManager
来收发本地广播效率更高(无需进行进程间通讯),而且无需考虑其余应用在收发广播时带来的任何安全问题。
开头说到LocalBroadcastManager
已经被废弃,须要引入依赖支持。可使用LiveData
来代替实现相关功能。这里就简单举例一下:
界面如上图所示,Fragment一、Fragment2以及包裹它们的Activity都共同持有一个ViewModel
。点击Fragment2中的按钮,Fragment1中的TextView和中间的分割线会发生变化。
建立一个须要用到的ViewModel
:
class LiveDataViewModel : ViewModel() {
/** * 只暴露不可变的LiveData给外部。 */
private val _value = MutableLiveData<String>()
val value: LiveData<String> get() = _value
fun setValue() {
_value.value = System.currentTimeMillis().toString()
}
}
复制代码
在Fragment2中更改value的值:
class LiveDataTwoFragment : Fragment() {
private lateinit var viewModel: LiveDataViewModel
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
return inflater.inflate(R.layout.fragment_live_data_two, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(LiveDataViewModel::class.java)
btnLiveData.setOnClickListener {
viewModel.setValue()
}
}
}
复制代码
在Fragment1和Activity中观察value的变化刷新UI:
class LiveDataActivity : AppCompatActivity() {
private lateinit var viewModel: LiveDataViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
viewModel = ViewModelProvider(this).get(LiveDataViewModel::class.java)
viewModel.value.observe(this, Observer {
val random = Random()
val r = random.nextInt(255)
val g = random.nextInt(255)
val b = random.nextInt(255)
viewLine.setBackgroundColor(Color.rgb(r, g, b))
})
}
}
复制代码
class LiveDataOneFragment : Fragment() {
private lateinit var viewModel: LiveDataViewModel
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? {
return inflater.inflate(R.layout.fragment_live_data_one, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(LiveDataViewModel::class.java)
viewModel.value.observe(requireActivity(), Observer { value ->
tvLiveData.text = value
})
}
}
复制代码
最终的效果以下:
文章到这里就已经结束了,这一次就LocalBroadcastManager的实现原理和与普通广播的区别作了介绍,以及相关的其余方案。文中的LiveData
+ViewModel
的实现只是其中一种方式而已,还有其余诸多优秀的方案能够选择,好比:EventBus
,RxBus
,LiveEventBus
等等。 因为我的水平的缘由,可能没法更深层次的去介绍,但愿能多多包涵。我也同广大的开发者同样,一步步地在慢慢成长。