前言
你好!
我是一只修仙的猿,欢迎阅读我的文章。
本篇文章的内容是讲解Broadcast的注册与发送的源码流程,不涉及Broadcast的使用。如果还没了解过广播如何使用的读者可以先去了解一下,但我相信不会的,因为广播在开发是很经常使用的。作为四大组件之一,广播的地位不言而喻。而了解其源码流程,可以更好地帮助我们了解他的工作流程,进而更好地了解Broadcast这个组件。这样在开发中,就会更加有自信去写代码,出现了bug也能更快地定位与解决。
本篇文章涉及大量的代码讲解,因而会显得非常枯燥无味。笔者加了大量的代码注释以及流程图来帮助读者理解。注释是非常重要的,代码外的讲解仅仅只是整体流程的把握,注释才是重点细节。还愿读者可以耐心阅读。
文章我以先把握整体流程,再到细节源码讲解的思路讲解。读者要先对整体流程有个把握,知道整体的方向再去阅读源码,这样不会在源码中丢失了方向,也不会不知道哪个地方是重点。读者亦可以边阅读边查看源码,加深印象。
笔者才疏学浅,有不同观点欢迎评论区或私信讨论。如需转载私信告知即可。
另外欢迎阅读笔者的个人博客一只修仙的猿的个人博客,更精美的UI,拥有更好的阅读体验。
本文大纲:
Broadcast注册流程
整体流程概述
注册流程相对来说比较简单,这里涉及到两个进程:
- Activity进程,即注册广播所在进程。
- AMS进程,AMS即ActivityManagerService,该进程为AMS所在进程。
注册的流程为Activity进程把广播接收器发送给AMS,AMS把广播接收器缓存在内部。流程比较简单。不同的进程当然就涉及到跨进程通信,API29源码使用的是AIDL(Android Interface Define Language),如果不熟悉的读者可以先去了解一下,有帮助对源码的理解。当然我也会在源码讲解中适当做解释,但不会深入讲,因为本文的主题是Broadcast工作流程讲解。
源码讲解
源码流程图
-
首先直接调用到ContextWrapper的registerReceiver方法,Activity是继承自ContextWrapper的。registerReceiver是Context的抽象方法,ContentWrapper继承自Content,但ContextWrapper本身并没有真正实现了Context的抽象方法,而是使用了装饰者模式。Context的真正实现是他内部的一个变量:mBase。这是ContextImpl的实例,所以最终的真正实现是在ContextImpl中。
/frameworks/base/core/java/android/content/ContextWrapper.java; public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { // 直接调用mBase的方法。mBase是ContextImpl的实例,也是Context的真正实现。 return mBase.registerReceiver(receiver, filter); }
这里补充一下为什么mBase是ContextImpl的实例。如果你阅读过Activity的启动流程(点击前往Activity的启动流程文章),可以知道是在ActivityThread创建Activity的时候,会给Activity初始化Context。下面看一下这部分的源码:
/frameworks/base/core/java/android/app/ActivityThread.java // ActivityThread调用此方法来启动Activity private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { ... // 在这里创建了Content,可以看到他的类型是ContentImpl ContextImpl appContext = createBaseContextForActivity(r); ... // 在这里把appContext给到了activity。说明这个就是Activity里面的Context的具体实现 appContext.setOuterContext(activity); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window, r.configCallback, r.assistToken); ... }
-
这一步是ContextImpl的逻辑。主要的任务就是构建IIntentReceiver并请求AMS来注册广播接收器。这里涉及到两个知识点:如何请求AMS,为什么不能直接把BroadcastReceiver传递给AMS。
第一个问题采用的是跨进程通信技术AIDL(Android Interface Define Language)安卓接口定义语言,主要用于跨进程通信,这里就不详细展开,不然就篇幅过大。读者可自行了解。
第二个问题,是因为广播涉及跨进程通信,而BoradcastReceiver本身并不支持跨进程,所以要进行封装一下。这个问题会在这一步的源码下面进行补充说明。
后面的逻辑就交给AMS去处理了。
/frameworks/base/core/java/android/app/ContextImpl.java; // 直接跳转 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { return registerReceiver(receiver, filter, null, null); } // 再跳转 public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) { return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext(), 0); } // 此方法主要是获取IIntentReceiver对象 // 为什么要获取IIntentReceiver上面已经有讲了。 // 并调用AMS在本地代理对象IActivityManagerService的方法来注册广播接收器 private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) { IIntentReceiver rd = null; if (receiver != null) { // 判断mPackageInfo和context是否为空,选择不同的方式获得IIntentReceiver if (mPackageInfo != null && context != null) { if (scheduler == null) { // 注意这里的scheduler是mMainThread.getHandler() // 他是ActivityThread的内部类H,这里先不管有什么用,注意一下就好,后面会讲到。 scheduler = mMainThread.getHandler(); } rd = mPackageInfo.getReceiverDispatcher( receiver, context, scheduler, mMainThread.getInstrumentation(), true); } else { ... } } try { // ActivityManager.getService()获取到的对象是AMS在本地的代理对象 // 通过代理对象可以直接跨进程访问AMS // 调用IActivityManagerService的方法来启动 final Intent intent = ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags); <