前言:

一开始的目标是解决各种各样的ANR问题的,但是我们知道,ANR总体上分有四种类型,这四种ANR类型有三种是和四大组件相对应的,所以,如果想了解ANR发生的根因,对安卓四大组件的实现原理必须要懂。

所以作者会写一系列的文章,来分析四大组建的实现原理,同时也会写相应的文章来讲解四种类型的ANR是如何发生的。

本篇文章的配套文章,介绍BroadCast中的ANR是如何产生的如下:

ANR系列之三:broadcast类型ANR产生原理讲解

本篇文章主要会讲以下内容:

1.广播的基本介绍,以及广播流程中涉及到的核心类介绍;

2.动态广播接收者的注册流程;

3.APP侧发送广播事件及系统侧接收;

4.无序广播的发送流程;

5.有序广播的发送流程;

6.静态广播的发送流程;

7.一些关于广播的扩展性问题;

8.总结

PS:本文基于android13的源码进行讲解。

一.基本概念和流程

1.1 广播流程核心类介绍:

首先,我们介绍整个流程中会涉及到的一些核心类。

ContextImp:Context的最终实现类,activity,application中的各种功能最终都是委托给其来处理的,广播功能自然也不例外。其负责和系统的AMS进行通信。

ActivityManagerService:负责所有应用的Activity的流程,实际上,四大组件都是由AMS负责处理和分发的。至于为什么不单独拆开,比如搞一个BroadcastManagerService,估计是觉得类似广播功能简单不需要单独设计Service吧,而且Service属于后台服务,还是越少越好的。

BroadcastReceiver:广播接收者

LoadedApk.ReceiverDispatcher:对动态广播接收者的封装对象

IntentResolver:用来记录所广播接收者的对象

BroadCastQueue:负责具体广播的分发工作。一种有三种,分别对应前台,后台,离线。

1.2 广播基本流程:

动态广播的整套流程相对于事件传播,还是算简单的,整套流程都在java层执行的,不涉及到native流程。

广播首先按照注册方式来区分有动态广播和静态广播两种,我们分开来讲;

1.2.1 动态广播基本流程

动态广播流程图可以概括成下面这张图:

首先是动态广播接收者注册,其向AMS注册广播接收器,AMS会把其注册信息记录到IntentResolver中。也就是说IntentResolver存放到了所有的动态广播接收器。

然后广播发出者向AMS发出广播,这时候AMS首先会通过IntentResolver查找出所有的广播接收者,然后会交给BroadcastQueue来执行传播流程。

1.2.2 静态广播基本流程

动态广播流程可以概括成下面这张图:

静态广播自然是没有广播接收者注册流程的。

当广播发出者向AMS发出广播后,AMS会首先通过PackageManager进行相关广播接收者相关配置的查询,得到一个接收者集合receivers,集合中元素的类型为ResolveInfo,然后遍历集合执行相关广播的传播流程。

这里要额外说明的是,静态广播和有序广播,实际上是执行了一样的流程,所以我们一起放到了第四章来讲解。

二.动态广播接收者注册流程

广播分为两种,动态广播和静态广播。

动态广播的流程,是首先注册广播接收者。这主要分为两块:

第一,是把接收者进行封装,注册到LoadedApk中的成员变量中。

第二,生成binder引用,注册到系统侧。

2.1 APP侧进行注册

2.1.1 注册基本流程

然后再发送广播,如果存在对应的广播Action,则会把事件传递到广播接收者上面。如果是静态广播,走的是另外一个流程,我们后面会讲。

整个动态广播接收者的注册的流程图如下,我们来一一解释下

2.1.2 生成binder引用并注册

上面有说,所有的Context类的操作,其实最终都会交给ContextImpl来处理,所以最终都会调用到ContextImpl的registerReceive方法。最终会调用到registerReceiverInternal()方法。

我们看一下这个方法:

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();

}

}

//注册binder应用和action到系统

final Intent intent = ActivityManager.getService().registerReceiver(

mMainThread.getApplicationThread(), mBasePackageName, rd,filter,...);

可以看到,只要接收者不为空,就会生成binder应用rd,最后通过AMS提供的binder方法注册到系统侧。AMS的后续执行流程,我们2.2中来讲。

2.1.3 广播接收者在APP侧注册

首先,我们先讲一下LoadedApk中和广播有关的几个成员变量:

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>>mReceivers = new ArrayMap<>();

private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers = new ArrayMap<>();

mReceivers:用来记录APP下所有动态广播接收者。key对应的是context,一般为activity,service等等。而value是一个ArrayMap,记录一个activity对象下所有的广播接收者,其映射关系是接收者对象和接收者封装对象。

mUnregisteredReceivers:和mReceivers类似,不过和这个是用来调试时才用的,似乎是故意让其取消失败时用的,这里就不扩展讲了。

一般情况下,mPackageInfo和context都不会为空的,所以我们看下通过getReceiverDispatcher方法返回binder的逻辑。

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,

Context context, Handler handler,

Instrumentation instrumentation, boolean registered) {

synchronized (mReceivers) {

LoadedApk.ReceiverDispatcher rd = null;

ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;

if (registered) {

//逻辑1

map = mReceivers.get(context);

if (map != null) {

rd = map.get(r);

}

}

if (rd == null) {

//逻辑3

rd = new ReceiverDispatcher(r, context, handler,

instrumentation, registered);

if (registered) {

if (map == null) {

map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();

//逻辑2

mReceivers.put(context, map);

}

//逻辑4

map.put(r, rd);

}

} else {

rd.validate(context, handler);

}

rd.mForgotten = false;

//逻辑5

return rd.getIIntentReceiver();

}

}

1.从mReceivers中检查context对象是否存在,这里的context对象就是activity或者application等,如果存在则返回map;

2.如果activity中是首次注册广播,那么自然是不存在这样的map的,就生成一个map然后注册到mReceivers中;

3.使用BroadcastReceiver来生成ReceiverDispatcher对象,所以ReceiverDispatcher中是持有BroadcastReceiver对象的;

4.把3中生成的ReceiverDispatcher对象加入到刚才创建的map中。

2.1.4 mReceivers和mUnregisteredReceivers存在的意义是什么?

这里解释一下,其实完成广播接收的时候,并不会从APP侧的mReceivers中进行查询。那么这里加入到mReceivers集合的意义是什么呢?

其目的是保证有GCRoot的引用持有广播接收者对象,从而不会被java虚拟机回收,而mReceivers就是这个GCRoot。

2.2 AMS一侧接收

AMS一侧,自然接收的方法也是registerReceiverWithFeature。该方法中,其实主要做了两件事:

首先,生成BroadcastFilter类型对象,注册到IntentResolver中的mFilters和mActionToFilter中。

BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,

receiverId, permission, callingUid, userId, instantApp, visibleToInstantApps);

if (rl.containsFilter(filter)) {

Slog.w(TAG, "Receiver with filter " + filter

+ " already registered for pid " + rl.pid

+ ", callerPackage is " + callerPackage);

} else {

rl.add(bf);

if (!bf.debugCheck()) {

Slog.w(TAG, "==> For Dynamic broadcast");

}

mReceiverResolver.addFilter(bf);

}

其次,进行粘性广播对象的注册。高版本的粘性广播谷歌已经废弃了,但是server侧的代码还没有删掉,所以这里也就不扩展讲解了,这里只列一下代码,如下:

这里也吐槽下谷歌的安卓源码项目,总是不断添加新功能,废弃的老功能不删,导致项目代码越来越大,编译后的项目也越来越大。

if (allSticky != null) {

ArrayList receivers = new ArrayList();

receivers.add(bf);

final int stickyCount = allSticky.size();

for (int i = 0; i < stickyCount; i++) {

Intent intent = allSticky.get(i);

BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(queue, intent, null,

null, null, -1, -1, false, null, null, null, null, OP_NONE, null,

receivers, null, 0, null, null, false, true, true, -1, false, null,

false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);

queue.enqueueParallelBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

}

}

2.3 APP侧取消广播接收者

取消注册的流程和注册时类似的,其实现方法在ContextImpl中,仍然主要分为两块:

public void unregisterReceiver(BroadcastReceiver receiver) {

//1

IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(getOuterContext(), receiver);

//2

ActivityManager.getService().unregisterReceiver(rd);

}

1.取消APP侧的的接收者注册;

2.通过binder通知系统侧取消接收者注册;

2.3.1 APP侧查找binder引用并取消接收者注册

public IIntentReceiver forgetReceiverDispatcher(Context context, BroadcastReceiver r) {

synchronized (mReceivers) {

//逻辑1

ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);

LoadedApk.ReceiverDispatcher rd = null;

if (map != null) {

rd = map.get(r);

if (rd != null) {

//逻辑2

map.remove(r);

if (map.size() == 0) {

mReceivers.remove(context);

}

}

}

}

首先从mReceivers中根据context找到对应的map;

然后从map中移除BroadcastReceiver对象,如果map.size=0,则可以直接移除map对象了。

2.3.2 通过binder通知系统侧

这个流程就比较简单了,直接通过AMS提供的binder方法unregisterReceiver向系统侧传递接收者的引用,从而取消注册。因为和注册的流程高度相似,所以就不扩展讲了,包括后面系统侧也不讲这一块的逻辑了。

2.4 小结:

所以,我们可以知道,动态广播最终是注册到了mReceiverResolver里面的成员变量中,其实现类是IntentResolver。首先注册到IntentResolver.mFilters上,然后根据action的类型,在具体负责注册到mActionToFilter或者mTypedActionToFilter上。

自然是注册到了mReceiverResolver这个对象上面,那么下面讲动态广播传播的流程,自然也会使用到这个对象。

三.广播事件发送以及系统侧接收

广播有多种类型,无序广播,有序广播,粘性广播等。因为粘性在安卓的高版本已经废弃,所以这里也就不讲了,我们这里只讲无序广播和有序广播的流程。

3.1 APP侧发送

无序广播,通过的是ContextImpl中sendBroadcast方法,而有序广播通过的是sendOrderedBroadcast方法。无论哪种,其实调用的都是AMS提供的binder方法broadcastIntentWithFeature,只是参数不同而已。其实甚于粘性广播调用的也是这个方法。方法代码如下,倒数第3个参数区分是否是有序广播,倒数第2的参数区分是否是粘性广播。

ActivityManager.getService().broadcastIntentWithFeature(

mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,

rd, initialCode, initialData, initialExtras, receiverPermissions,

null /*excludedPermissions=*/, null, appOp, options, true, false,

user.getIdentifier());

3.2 系统侧接收

AMS一侧自然是broadcastIntentWithFeature方法接收的。主要执行的如下的流程:

broadcastIntentWithFeature首先会通知到AMS的broadcastIntentLocked方法中。

在该方法中,我们上面有讲过,动态广播数注册到mReceiverResolver中的。所以这里自然先从mReceiverResolver中查询,看是否存在接收者,如果存在,加入到registeredReceivers集合中。

代码如下:

registeredReceivers = mReceiverResolver.queryIntent(intent,

resolvedType, false /*defaultOnly*/, userId);

3.3 系统侧处理广播

broadcastIntentLocked方法是系统册处理广播的核心,下面代码的讲解也主要都是在broadcastIntentLocked方法中执行的。

首先,会进行一些安全性的检查,比如进程是否存在,是否有对应广播的权限,发送的是否是保护性广播(比如开机广播这种就不可能允许任意进程随便发)等等,如果不具有对应的权限,则返回失败。

然后进行粘性广播的逻辑判断,粘性广播的逻辑这里就不扩展讲解了。

接下来会去查找静态广播接收者和动态广播接收者。我从代码中可以看到有两个集合,分别是receivers和registeredReceivers,对应的分别就是查找出来的静态广播接收者和动态广播接收者。

List receivers = null;

List<BroadcastFilter> registeredReceivers = null;

// Need to resolve the intent to interested receivers...

if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)

== 0) {

receivers = collectReceiverComponents(

intent, resolvedType, callingUid, users, broadcastAllowList);

}

if (intent.getComponent() == null) {

if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {

//这里是特殊逻辑,不用关心

} else {

registeredReceivers = mReceiverResolver.queryIntent(intent,

resolvedType, false /*defaultOnly*/, userId);

}

}

由于动态广播和静态广播的处理逻辑是不一样的,代码也是分开的,所以我们接下来分别来讲这两者的流程。

四.动态广播事件传递流程

4.1 本章概要

本章内容主要都是发生在AMS中的broadcastIntentLocked方法中。

动态广播流程中,首先我们要查找动态广播的接受者,这个我们放到4.2小节中来讲;

然后开启广播的发送流程,因为有序广播和无序广播的流程是不一样的,所以4.3我们主要讲一下两者的区别;

4.4中,我们讲一下无序广播的发送流程,有序广播的流程我们下一章来讲;

最后的4.5小节,我们讲一下server侧的广播是如何传递到APP一侧的。

4.2 查找动态广播接收者

这里对应的核心就是上面的queryIntent方法中。queryIntent方法中,按照不同类型进行查询:

如果resolvedType不为空,则从mTypeToFilter,mBaseTypeToFilter,mTypedActionToFilter,mWildTypeToFilter等集合查询。

如果scheme不为空,则从mSchemeToFilter集合中查询;

如果resolvedType为空,则从mActionToFilter集合中查询;

最后,会把上面查找的所有结果按照优先级进行排序,最终进行返回,返回值就是registeredReceivers集合。如果同一个广播满足多个条件,也只会在集合中存在一份。

4.3 分别开启无序广播和有序广播发送流程

找到了广播接收者之后,我们就可以准备开始广播流程了。这里无序广播和有序广播的流程是不一样的。

4.3.1 首先我们来看下无序广播:

//代码有删减,只保留核心内容

int NR = registeredReceivers != null ? registeredReceivers.size() : 0;

if (!ordered && NR > 0) {

final BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(...);

...

queue.enqueueParallelBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

}

我们可以看到,首先查找对应的BroadcastQueue,BroadcastQueue是负责具体发送任务的。分别前台,后台,离线三种。这里对应的其实也就是我们发送广播时的前台/后台广播。

然后生成广播对象BroadcastRecord,加入到BroadcastQueue中mParallelBroadcasts集合中。

最后,通过scheduleBroadcastsLocked开启广播流程。

4.3.2 然后我们来看下有序广播流程

首先,把上面动态广播接收者集合registeredReceivers中的对象,都加入到receivers中。也就是说,后续流程中receivers集合其实是包含了有序广播和静态广播的集合,receivers中同时存在两种元素类型BroadcastFilter和ResolveInfo。相关代码如下:

void broadcastIntentLocked(){

...

while (ir < NR) {

if (receivers == null) {

receivers = new ArrayList();

}

receivers.add(registeredReceivers.get(ir));

ir++;

}

...

}

然后处理receivers集合中的对象,该流程和无序广播的流程基本上是一致的。

首先根据receivers生成广播对象BroadcastRecord,然后加入到BroadcastQueue.BroadcastDispatcher中的mOrderedBroadcasts集合中,最后通过scheduleBroadcastsLocked方法开启广播流程。

if ((receivers != null && receivers.size() > 0)

BroadcastQueue queue = broadcastQueueForIntent(intent);

BroadcastRecord r = new BroadcastRecord(...);

queue.enqueueOrderedBroadcastLocked(r);

queue.scheduleBroadcastsLocked();

}

4.3.3 最后我们来看下两者区别

有序的流程前面和无序广播前面的流程是一样的,也是说4.2.4之前,有序和无序执行的是一样的流程。而区别就是从4.2.4开始的,我花了一张简单的流程图,来展示一下两者的区别。

有序广播和无序广播的区别,就是加入到的BroadcastQueue的集合对象不一样,有序广播会把查找到的广播接对象BroadcastRecord加入到BroadcastDispatcher中的mOrderedBroadcasts集合,而无序广播则会加入到mParallelBroadcasts中。

但是后续的广播发送流程是一致的,两者都会调用scheduleBroadcastsLocked方法开启广播流程,该流程中会先处理无序广播的流程,然后再执行有序广播的流程,最后执行静态广播的流程。

4.4 发送无序广播流程

本章主要讲无序广播的流程,有序广播的我们下一章再讲。所以通过scheduleBroadcastsLocked方法开启广播流程后,我们接着往下看:

1.scheduleBroadcastsLocked中首先判断是否正在发送的流程中。如果正在发送流程中则直接忽略本次操作,否则进入发送流程。

public void scheduleBroadcastsLocked() {

if (mBroadcastsScheduled) {

return;

}

mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));

mBroadcastsScheduled = true;

}

2.通过handler转发之后,切换到主线程,执行processNextBroadcast方法。processNextBroadcast方法中加了个锁,调用processNextBroadcastLocked方法,解决多线程问题。

为什么会有多线程问题?实话实说,我认为这里是一个源码中疏忽的地方,因为经过handler的切换,这里一定会是在系统侧的主线程执行,就不可能存在多线程的问题。

//线程切换

case BROADCAST_INTENT_MSG: {

processNextBroadcast(true);

//加锁

private void processNextBroadcast(boolean fromMsg) {

synchronized (mService) {

processNextBroadcastLocked(fromMsg, false);

}

}

3.因为无序广播和有序广播其实都通过processNextBroadcastLocked方法来执行发送流程的,所以这里讲processNextBroadcastLocked方法的时候,先只讲无序广播的部分,下一小节再讲有序广播的部分。

该方法中,首先遍历mParallelBroadcasts集合,针对每个广播事件记录BroadcastRecord,通过deliverToRegisteredReceiverLocked方法进行对应的客户端进程(mParallelBroadcasts集合中的BroadCastRecord就是我们刚刚添加的)。

至此,动态广播中的无序广播发送流程已经完成,此时的客户端已经成功收到了无序广播。deliverToRegisteredReceiverLocked方法负责把具体的广播事件发送到APP一侧,这个流程我们4.5中来讲。

4.5 把广播事件传递给客户端

这一小节其实主要就是针对deliverToRegisteredReceiverLocked方法的讲解。

首先是进行一列的检查,如下

1.进行相关的权限检查,看发送者具有有接收者所要求的相关权限;

if (filter.requiredPermission != null) {

int perm = mService.checkComponentPermission(filter.requiredPermission,

r.callingPid, r.callingUid, -1, true);

if (perm != PackageManager.PERMISSION_GRANTED) {

Slog.w(TAG, "Permission Denial: broadcasting "

...

skip = true;

} else {

...

}

}

2.检查接收者是否为空

if (!skip && (filter.receiverList.app == null || filter.receiverList.app.isKilled()

|| filter.receiverList.app.mErrorState.isCrashing())) {

Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r

+ " to " + filter.receiverList + ": process gone or crashing");

skip = true;

}

3.进行一系列其他的安全检查

4.检查启动安全模式,高版本是严格,低版本放的比较松。之前作者就在这里踩过坑。

if (!skip) {

final int allowed = mService.getAppStartModeLOSP(

info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,

info.activityInfo.applicationInfo.targetSdkVersion, -1, true, false, false);

if (allowed != ActivityManager.APP_START_MODE_NORMAL) {

// We won't allow this receiver to be launched if the app has been

// completely disabled from launches, or it was not explicitly sent

// to it and the app is in a state that should not receive it

// (depending on how getAppStartModeLOSP has determined that).

if (allowed == ActivityManager.APP_START_MODE_DISABLED) {

Slog.w(TAG, "Background execution disabled: receiving "

+ r.intent + " to "

+ component.flattenToShortString());

skip = true;

} else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)

|| (r.intent.getComponent() == null

&& r.intent.getPackage() == null

&& ((r.intent.getFlags()

& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)

&& !isSignaturePerm(r.requiredPermissions))) {

mService.addBackgroundCheckViolationLocked(r.intent.getAction(),

component.getPackageName());

Slog.w(TAG, "Background execution not allowed: receiving "

+ r.intent + " to "

+ component.flattenToShortString());

skip = true;

}

}

}

5.调用performReceiveLocked方法完成向APP的发送。

performReceiveLocked方法中,首先获取APP侧的Binder对象IApplicationThread,然后通过其binder方法scheduleRegisteredReceiver发送给APP一侧,或者直接通过binder对象完成跨进程的通信。

void performReceiveLocked(...) throws RemoteException {

if (app != null) {

final IApplicationThread thread = app.getThread();

if (thread != null) {

try {

thread.scheduleRegisteredReceiver(receiver, intent, resultCode,

data, extras, ordered, sticky, sendingUser,

app.mState.getReportedProcState());

...

}else{

receiver.performReceive(intent, resultCode, data, extras, ordered,

sticky, sendingUser);

}

}

6.APP一侧自然是ActivityThread中的scheduleRegisteredReceiver方法完成的接收,其逻辑也很简单,直接交给binder对象receiver来处理,这逻辑,就和5中的else逻辑一致了。

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,

int resultCode, String dataStr, Bundle extras, boolean ordered,

boolean sticky, int sendingUser, int processState) throws RemoteException {

updateProcessState(processState, false);

receiver.performReceive(intent, resultCode, dataStr, extras, ordered,

sticky, sendingUser);

}

7.receiver是一个binder,其server是在LoadedApk.ReceiverDispatcher中的InnerReceiver类。所以,最终是InnerReceiver中的performReceive方法收到了这个binder通知。方法如下:

@Override

public void performReceive(...) {

final LoadedApk.ReceiverDispatcher rd;

...

if (rd != null) {

rd.performReceive(intent, resultCode, data, extras,

ordered, sticky, sendingUser);

} else {

...

}

}

我们所以看到,又调用到了LoadedApk.ReceiverDispatcher中的performReceive方法。

8.LoadedApk.ReceiverDispatcher的performReceive方法如下:

public void performReceive(...){

...

if (intent == null || !mActivityThread.post(args.getRunnable())) {

if (mRegistered && ordered) {

IActivityManager mgr = ActivityManager.getService();

if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,

"Finishing sync broadcast to " + mReceiver);

args.sendFinished(mgr);

}

}

}

我们可以看到,通过handler进行一个线程切换,切换到主线程执行Args中的Runnable。

9.那么我们最后就来看下这个在主线程中将要被执行的Args中的Runnable,代码如下:

public final Runnable getRunnable() {

return () -> {

final BroadcastReceiver receiver = mReceiver;

final boolean ordered = mOrdered;

...

final IActivityManager mgr = ActivityManager.getService();

final Intent intent = mCurIntent;

...

try {

ClassLoader cl = mReceiver.getClass().getClassLoader();

intent.setExtrasClassLoader(cl); intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent),

mContext.getAttributionSource());

setExtrasClassLoader(cl);

receiver.setPendingResult(this);

receiver.onReceive(mContext, intent);

} catch (Exception e) {

...

}

if (receiver.getPendingResult() != null) {

finish();

}

};

}

通过代码,我们可以看到,主要做了两件事:

首先,因为其持有BroadcastReceiver对象receiver,所以直接通知了BroadcastReceiver的onReceive方法。

然后,如果是有序广播,还要调用finish方法通知系统已完成此次广播的传递流程。

五.有序广播的传播流程

5.1 有序广播的主要流程

有序广播,也是通过scheduleBroadcastsLocked方法开启的流程,其前面的流程执行流程和无序广播是一样的,区别是从processNextBroadcastLocked方法开始的。

processNextBroadcastLocked中首先处理有序广播, 无序广播处理完成后,就开始有序广播的处理流程了,甚至静态广播的处理流程也在该方法的最后。

有序广播按照我的理解,主要分为以下4个阶段:

1.找到待发送的有序广播对象;

2.执行一系列的安全判断;

3.发送前的准备操作;

4.执行有序广播的发送;

5.2 找到待发送的广播对象

有序广播的发送流程,主要是BroadcastDispatcher来负责的。其首先通过getNextBroadcastLocked方法找出下一个该发送哪个广播,这个广播对象就是BroadcastRecord。

r = mDispatcher.getNextBroadcastLocked(now);

该方法中,只要执行以下的逻辑:

public BroadcastRecord getNextBroadcastLocked(final long now) {

//逻辑1

if (mCurrentBroadcast != null) {

return mCurrentBroadcast;

}

final boolean someQueued = !mOrderedBroadcasts.isEmpty();

BroadcastRecord next = null;

//逻辑2

if (!mAlarmBroadcasts.isEmpty()) {

next = popLocked(mAlarmBroadcasts);

if (DEBUG_BROADCAST_DEFERRAL && next != null) {

Slog.i(TAG, "Next broadcast from alarm targets: " + next);

}

}

//逻辑3

if (next == null && !mDeferredBroadcasts.isEmpty()) {

// We're going to deliver either:

// 1. the next "overdue" deferral; or

// 2. the next ordinary ordered broadcast; *or*

// 3. the next not-yet-overdue deferral.

for (int i = 0; i < mDeferredBroadcasts.size(); i++) {

Deferrals d = mDeferredBroadcasts.get(i);

if (now < d.deferUntil && someQueued) {

// stop looking when we haven't hit the next time-out boundary

// but only if we have un-deferred broadcasts waiting,

// otherwise we can deliver whatever deferred broadcast

// is next available.

break;

}

if (d.broadcasts.size() > 0) {

next = d.broadcasts.remove(0);

// apply deferral-interval decay policy and move this uid's

// deferred broadcasts down in the delivery queue accordingly

mDeferredBroadcasts.remove(i); // already 'd'

d.deferredBy = calculateDeferral(d.deferredBy);

d.deferUntil += d.deferredBy;

insertLocked(mDeferredBroadcasts, d);

if (DEBUG_BROADCAST_DEFERRAL) {

Slog.i(TAG, "Next broadcast from deferrals " + next

+ ", deferUntil now " + d.deferUntil);

}

break;

}

}

}

//逻辑4

if (next == null && someQueued) {

next = mOrderedBroadcasts.remove(0);

if (DEBUG_BROADCAST_DEFERRAL) {

Slog.i(TAG, "Next broadcast from main queue: " + next);

}

}

mCurrentBroadcast = next;

return next;

}

1.如果当前的广播还未处理完成,则直接返回当前正在处理的广播mCurrentBroadcast。

2.如果有定时广播,则从定制广播中取出第一个返回。

3.如果有延迟广播,则从延迟广播中取出第一个返回。

4.上面都没有的情况下,最后轮到了有序广播。从有序广播的队列mOrderedBroadcasts中取出第一个返回。上面我们有讲到,mOrderedBroadcasts集合的加入,是在5.3中完成的。

5.3 执行一系列的安全判断

找到了待发送的广播对象后,就开始执行一系列的安全检查了。

//流程1

if (r == null) {

return;

}

//流程2

int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;

if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {

if ((numReceivers > 0) &&

(now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {

broadcastTimeoutLocked(false); // forcibly finish this broadcast

forceReceive = true;

r.state = BroadcastRecord.IDLE;

}

//流程3

if (r.state != BroadcastRecord.IDLE) {

return;

}

//流程4

if (r.receivers == null || r.nextReceiver >= numReceivers

|| r.resultAbort || forceReceive) {

...

mDispatcher.retireBroadcastLocked(r);

r = null;

looped = true;

}

1.如果对象为空,则直接结束掉当前流程;

2.进行相关的超市判断,如果超时,则把forceReceive设置为true。

3.如果当前广播对象不属于空闲, 则说明已经进入到了发送流程中,则直接结束掉当前流程。

4.最终进行一系列的安全判断,满足下面任意条件,则进入到异常场景并进行异常处理。异常处理的最后,会把r设置为null,进行下一轮循环找到合适的BroadcastRecord对象。

广播对象的接收者为空;

广播对象当前执行到的位置,已经大于等于接收者的总数;

广播对象被放弃;

广播对象已经满足超时条件了,2中已经进行了判断。

5.4 发送前的准备操作;

经过一系列的判断,找到了合法的将要被处理的广播对象r,则首先进行一些发送前的准备操作。

1.因为这里是有序广播的流程,所以首先看当前已经执行到了第几个接收者了。

// Get the next receiver...

int recIdx = r.nextReceiver++;

2.重置发送时间,广播的超时是和这个值相关的。

r.receiverTime = SystemClock.uptimeMillis();

3.如果有序广播是第0位,则会更新两个时间值,如下:

if (recIdx == 0) {

r.dispatchTime = r.receiverTime;

r.dispatchClockTime = System.currentTimeMillis();

...

}

更新BroadcastRecord的dispatchTime和dispatchClockTime时间。dispatchTime时间可以理解为开始执行广播流程的时间,而上面的receiverTime时间可以理解为通知单个广播接受者使用的时间。

何时超时,我们放到广播类型的ANR那篇文章中来讲。

4.发送一个延时handler消息,用来监测是否整个广播流程是否超时。

if (! mPendingBroadcastTimeoutMessage) {

setBroadcastTimeoutLocked(timeoutTime);

}

5.5 执行有序广播的发送

好了,经过一系列的操作,我们终于可以开始发送广播了。

1.取出下一个广播接收者。判断其类型,如果是BroadcastFilter类型,说明是动态广播接受者,则执行发送流程。如果是ResolveInfo类型,则说明是静态广播接收者,我们第六章来讲。

final BroadcastOptions brOptions = r.options;

final Object nextReceiver = r.receivers.get(recIdx);

if (nextReceiver instanceof BroadcastFilter) {

//动态有序广播流程

deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);

return;

}

//静态广播流程

2.执行最终的发送流程。

最终的发送流程方法都在deliverToRegisteredReceiverLocked中,这个方法我们在4.3小节中讲了,这里就不赘述了。

六.静态广播的传播流程

6.1 静态广播的主要流程

静态广播的整个执行流程其实也是在processNextBroadcastLocked方法中,它和无序广播,有序广播是一样的,只不过在这个方法中最后被执行。

上面说5.5中讲到,从BroadcastRecord中的receivers中取出对象,如果是BroadcastFilter类型执行动态有序广播流程,如果是ResolveInfo类型,则会执行静态广播流程了。

这里要注意的是,无论静态广播,还是有序广播,都属于同一个BroadcastRecord对象。也就是说发送一个有序广播,其接收者有可能是静态广播接收者,也有可能是动态广播接收者,而且属于同一批次的接收者。

静态广播主要有如下流程:

1.静态广播的准备工作

2.接收者进程存活情况下通知接收者;

3.接收者进程不存活情况下则拉起接收者进程;

4.权限检查详解。

6.2 静态广播的准备工作

6.2.1构建对象

既然不是BroadcastFilter类型,那么一定是ResolveInfo类型,所以直接转换为ResolveInfo对象。

然后构建ComponentName对象供后面使用。

ResolveInfo info = (ResolveInfo)nextReceiver;

ComponentName component = new ComponentName(

info.activityInfo.applicationInfo.packageName,

info.activityInfo.name);

6.2.2 权限检查

接下来,会执行一系列的权限检查。这一块检查会有很多项目,因为不算事是核心流程,所以我们放到6.5中专门来讲,这里暂且先跳过权且认为权限检查已通过。

boolean skip = false;

//各种判断,如果不通过,就会把skip改为true

if (skip) {

r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;

r.receiver = null;

r.curFilter = null;

r.state = BroadcastRecord.IDLE;

r.manifestSkipCount++;

scheduleBroadcastsLocked();

return;

}

6.2.3 准备工作

最后做一些发送前的准备工作,首先更新BroadcastRecord中的一些状态值:

r.manifestCount++;

r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;

r.state = BroadcastRecord.APP_RECEIVE;

r.curComponent = component;

r.curReceiver = info.activityInfo;

然后修改对应包名的状态,使其暂时成为不可杀死应用。

// Broadcast is being executed, its package can't be stopped.

try {

AppGlobals.getPackageManager().setPackageStoppedState(

r.curComponent.getPackageName(), false, r.userId);

}

6.3 接收者进程存活情况下通知接收者

如果APP进程还存活,则会通过processCurBroadcastLocked方法执行发送流程,发送广播给对应的进程。

if (app != null && app.getThread() != null && !app.isKilled()) {

processCurBroadcastLocked(r, app);

}

我们再来看一下processCurBroadcastLocked方法,如下:

private final void processCurBroadcastLocked(BroadcastRecord r,

ProcessRecord app) throws RemoteException {

...

//流程1

final IApplicationThread thread = app.getThread();

if (thread == null) {

throw new RemoteException();

}

if (app.isInFullBackup()) {

skipReceiverLocked(r);

return;

}

//流程2

r.receiver = thread.asBinder();

r.curApp = app;

final ProcessReceiverRecord prr = app.mReceivers;

prr.addCurReceiver(r);

app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);

mService.updateLruProcessLocked(app, false, null);

...

r.intent.setComponent(r.curComponent);

boolean started = false;

try {

//流程3

thread.scheduleReceiver(new Intent(r.intent), ...);

started = true;

} finally {

...

}

}

主要做了三件事:

1.检查对应进程的binder是否存在,不存在就无法和APP进行通信,自然抛出异常终止流程。

2.更新BroadcastRecord对象的属性值,以及BroadcastRecord中Intent的属性值,这样最终发送到APP侧后,APP可以知道应该启动哪个接收者。

3.通过binder方法scheduleReceiver完成跨进程通信,通知APP一侧。

6.4 接收者进程不存活情况下通知接收者

如果APP进程不存活,则通过两步操作来完成通知接收者的操作:

首先拉起广播接收者所在的进程;

然后在拉起后通知APP一侧。

6.4.1 拉起广播接收者所在的进程

//流程1

r.curApp = mService.startProcessLocked(...)

//流程2

if (r.curApp == null) {

finishReceiverLocked(r, r.resultCode, r.resultData,r.resultExtras, r.resultAbort, false);

scheduleBroadcastsLocked();

r.state = BroadcastRecord.IDLE;

return;

}

//流程3

mPendingBroadcast = r;

mPendingBroadcastRecvIndex = recIdx;

主要做了三件事:

1.直接通过AMS进行拉起接收者所属进程的操作。

2.如果拉起失败,则直接结束掉当前的广播流程

3.拉起成功,则修改mPendingBroadcast和mPendingBroadcastRecvIndex为当前广播对象的值。这样下一次执行广播流程,就会处理。

6.4.2 PendingBroadcast阻塞和检查

我们回头看一下processNextBroadcastLocked方法,发现在完成无序广播的发送后,有这样一段代码来处理mPendingBroadcast对象。

//流程1

if (mPendingBroadcast != null) {

boolean isDead;

//流程2

if (mPendingBroadcast.curApp.getPid() > 0) {

synchronized (mService.mPidsSelfLocked) {

ProcessRecord proc = mService.mPidsSelfLocked.get(

mPendingBroadcast.curApp.getPid());

isDead = proc == null || proc.mErrorState.isCrashing();

}

} else {

//流程3

final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(

mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);

isDead = proc == null || !proc.isPendingStart();

}

if (!isDead) {

//流程4

// It's still alive, so keep waiting

return;

} else {

//流程5

mPendingBroadcast.state = BroadcastRecord.IDLE;

mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;

mPendingBroadcast = null;

}

}

1.mPendingBroadcast不为空,说明刚刚启动过进程存在静态广播。所以这里推测,APP进程启动后,应该会通知系统侧进行某个流程,置空mPendingBroadcast并完成发送,这个我们后面来讲。

2.pid>0只是代表进程创建成功,但是并不代表进程存活,所以还要再次检查一下。最后更新到isDead这个变量上。

3.如果创建不成功,并不代表当前执行时还未成功,则从进程列表中查询一边,判断进程是否存活更新到isDead上。

4.如果进程存活,则直接返回,继续等待。也就是说,静态广播和有序广播都被阻塞住。

5.如果进程不存活,则直接结束流程并且把相关变量进行重置。

这个流程我们可以得出以下的结论:

1.mPendingBroadcast存在时,有序广播和静态广播是会被阻塞住的。

2.如果mPendingBroadcast所对应的进程启动失败,则会立马进行重置,避免阻塞。

6.4.3 通知APP一侧

接着我们看一下,到底是哪里来处理mPendingBroadcast并完成广播发送的。

这里我们得稍微回顾一下APP的启动流程了,如果对APP启动流程感兴趣,可以看一下下面的这篇文章。

android源码学习- APP启动流程

我们这里稍微讲一下,APP进程启动后,会调用ActivityThread的main方法,main方法中会调用binder方法attachApplication通知系统侧APP进程创建完成,这里就会调用到AMS中的attachApplication方法,经过加锁后调用到attachApplicationLocked方法,看一下这个方法:

private boolean attachApplicationLocked(...) {

//逻辑1

thread.bindApplication(...)

//逻辑2

didSomething |= sendPendingBroadcastsLocked(app);

...

}

1.通知APP区创建Application完成初始化操作。

2.去执行pending的广播的发送。

逻辑2中,会最终调用到BroadcastQueue中的sendPendingBroadcastsLocked方法,我们看下这个方法:

public boolean sendPendingBroadcastsLocked(ProcessRecord app) {

boolean didSomething = false;

//逻辑1

final BroadcastRecord br = mPendingBroadcast;

if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {

if (br.curApp != app) {

return false;

}

try {

//逻辑2

mPendingBroadcast = null;

//逻辑3

processCurBroadcastLocked(br, app);

didSomething = true;

} catch (Exception e) {

...

}

}

return didSomething;

}

首先,获取mPendingBroadcast对象(逻辑1);

然后把mPendingBroadcast置为null,这也正好对应了我们上一小节的推测(逻辑2);

最终调用processCurBroadcastLocked方法完成广播的发送,processCurBroadcastLocked方法我们6.3中已经讲过了(逻辑3)。

6.5 静态广播权限检查

上面讲到了,发送静态广播需要对接收者经验很多的权限检查,因为检查条件太多了,所以我们挑几个重要的来讲。

1.检查接收者targetSDK版本的问题,如果不再发送广播的目标范围内,则检查不通过。

ResolveInfo info = (ResolveInfo)nextReceiver;

if (brOptions != null &&

(info.activityInfo.applicationInfo.targetSdkVersion

< brOptions.getMinManifestReceiverApiLevel() ||

info.activityInfo.applicationInfo.targetSdkVersion

> brOptions.getMaxManifestReceiverApiLevel())) {

...

skip = true;

}

2.检查发送进程和接收进程是否存在关联关系。如果两者其一属于系统集成,则被允许。

if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,component.getPackageName(), info.activityInfo.applicationInfo.uid)) {

skip = true;

}

3.检查接收者是否声明了export,以及是否具体接收者组件所声明的权限。也就是说,只有赋予了接收者APP对应的权限,才能够接收。

int perm = mService.checkComponentPermission(info.activityInfo.permission,

r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,

info.activityInfo.exported);

if (!skip && perm != PackageManager.PERMISSION_GRANTED) {

skip = true;

} else if (!skip && info.activityInfo.permission != null) {

if (opCode != AppOpsManager.OP_NONE &...) {

skip = true;

}

}

4.即时应用是不允许发送的

if (!skip && r.callerInstantApp

&& (info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0

&& r.callingUid != info.activityInfo.applicationInfo.uid) {

Slog.w(TAG, "Instant App Denial: receiving "

+ r.intent

+ " to " + component.flattenToShortString()

+ " requires receiver have visibleToInstantApps set"

+ " due to sender " + r.callerPackage

+ " (uid " + r.callingUid + ")");

skip = true;

}

5.发送者进程如果出现Crash,也是不允许发送的

if (r.curApp != null && r.curApp.mErrorState.isCrashing()) {

skip = true;

}

6.这一块按照我的理解是是否暂停了接收者的应用吧,应用暂停则不可接收(应用暂停时android12.1开始推出的新功能)。

isAvailable = AppGlobals.getPackageManager().isPackageAvailable(

info.activityInfo.packageName,UserHandle.getUserId(info.activityInfo.applicationInfo.uid));

if (!isAvailable) {

skip = true;

}

7.检查APP检查模式。基本上是按照APP配置的targetSDK版本来区分的,高版本检查很严格,低版本松,之前作者就在这里踩过坑。

final int allowed = mService.getAppStartModeLOSP(...);

if (allowed != ActivityManager.APP_START_MODE_NORMAL) {

skip = true;

}else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)

|| (r.intent.getComponent() == null

&& r.intent.getPackage() == null

&& ((r.intent.getFlags()

& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)

&& !isSignaturePerm(r.requiredPermissions))) {

skip = true;

}

8.关闭应用类型的广播,收到运行状态的限制。

if (!skip && !Intent.ACTION_SHUTDOWN.equals(r.intent.getAction())

&& !mService.mUserController

.isUserRunning(UserHandle.getUserId(info.activityInfo.applicationInfo.uid),0 )) {

skip = true;

}

9.非系统进程,还要检查接受者是否有发送者所要求的权限

if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID &&

r.requiredPermissions != null && r.requiredPermissions.length > 0) {

...代码略

skip = true;

}

我们可以看到,静态广播的各种限制,要比动态广播多得多。这是因为静态广播可以直接唤起一个新的APP应用,这个影响面还是很大的。比如我一个APP如果注册了静态广播中的开屏广播,那么每次每次亮屏都会启动我的APP,实现了一个保活,这影响面无疑是很大的,所以一定要做一定的限制。目前普通的APP基本上已经无法通过注册静态广播的方式来使用了。

七.关于BroadCast扩展性问题

1.如果两个应用注册同样action的广播接收者,会怎样?

答:首先要去分有序广播还是无序广播。如果无序广播,那么所有的的广播接收者都会收到通知。如果是有序广播,那么就会按照优先级依次收到通知。

2.广播是否可靠?

答:不可靠。进程如果不存在或者挂掉直接结束了流程,或者发送者进程挂掉也会导致静态广播接收者收不到。

3.动态广播接收者优先级99,静态广播接收者优先级100,哪个会先收到?

答:虽然流程上,动态广播会优先执行。但是由于有序广播,其实每次只处理一个广播对象中广播接收者集合中的一条。所以仍然会按照广播接收者集合中的顺序来执行。相关核心代码如下:

final Object nextReceiver = r.receivers.get(recIdx);

4.使用静态广播时,Application中有耗时操作会有什么问题?

答:这要有一个前提条件,就是APP进城不存在,存在的话就不会走Application流程。

虽然系统侧会连续发送两条任务(创建application和发送广播两条任务)给APP一侧,但是由于都在主线程执行,所以执行顺序上并不会出现问题。

但是由于静态广播创建新进程时是阻塞的,后面的有序广播时不能发送的,因此系统会有ANR超时机制,时间过长是会导致ANR的。

5.发送完广播后发送者进程闪退了,接收者能否收到?

答:参照6.5中静态广播严格的检查,发送者crash了静态广播应该是收不到的,动态可以。

6.前台广播和后台广播有什么区别?

答:从目前代码看来,前台和后台所归属的BroadcastQueue是不同的,所以相互之间不会阻塞。即发送前台广播阻塞时并不会影响后台广播的发送。另外前后台广播和后台广播的时间是不一样的,分别为10S和60S。

// How long we allow a receiver to run before giving up on it.

static final int BROADCAST_FG_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

static final int BROADCAST_BG_TIMEOUT = 60 * 1000 * Build.HW_TIMEOUT_MULTIPLIER;

其它流程基本没有区别。

7.发送无序广播,并且注册多个带不同优先级的静态广播接收者,优先级是否会生效?

答:是生效的,上面有介绍。静态广播和有序广播,走的其实是一个流程。所以对于静态广播接收者,哪怕发送的是普通广播类型,一样会按照优先级进行分发。

但是如果此时同样注册有动态广播接收者,那么一定是动态广播接收者先收到这个广播,然后静态广播接收者在按照优先级依次完成接收。

8.abortBroadcast是如何结束掉广播流程的?

答:abortBroadcast方法会修改BroadcastReceiver中的属性值mAbortBroadcast为true。一次有序广播事件APP完成后,都会发送sendFinish方法通知系统侧,我们看一下这个方法。

public void sendFinished(IActivityManager am) {

if (mOrderedHint) {

am.finishReceiver(mToken, mResultCode, mResultData, mResultExtras,

mAbortBroadcast, mFlags);

} else {

am.finishReceiver(mToken, 0, null, null, false, mFlags);

}

}

可以看到如果是有序广播会把mAbortBroadcast这个值传递给系统侧。AMS收到会,会转交给处理队列BroadcastQueue中的finishReceiverLocked方法,最终更新到广播事件对象BroadcastQueue中的属性值resultAbort上。

if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {

r.resultAbort = resultAbort;

}

后续BroadcastQueue队列在处理广播对象时,识别到resultAbort=true就会略过。

if (r.receivers == null || r.nextReceiver >= numReceivers

|| r.resultAbort || forceReceive) {

...

r = null

continue;

}

9.有序和无序广播是怎么区分的?

答:sendOrderedBroadcast和sendBroadcast两个方法,其实最终broadcastIntentWithFeature方法中,就是一个参数不一样,倒数第三个boolean serialized。sendOrderedBroadcast为true,sendBroadcast为false。

10.静态广播的接收者对象是否是同一个?

答:结论有可能和你想象的并不一样。静态广播,每次的广播接收者都是新生成的,所以不在再静态广播的构造方法中做耗时操作。

receiver = packageInfo.getAppFactory().instantiateReceiver(cl, data.info.name, data.intent);

public @NonNull BroadcastReceiver instantiateReceiver(...)

throws InstantiationException, IllegalAccessException, ClassNotFoundException {

return (BroadcastReceiver) cl.loadClass(className).newInstance();

}

11.不取消注册动态广播会造成泄漏吗?

答:我们来一个实际的场景。ActivityA中注册了一个广播,点击按钮后没有取消广播注册,就直接跳转ActivityB并且同时finish掉A。此时A会被泄漏吗?

答案是不会,系统已经帮我考虑了这种情,所以做好了兼容。具体代码如下:

//1.destory方法

public void handleDestroyActivity(){

((ContextImpl) c).scheduleFinalCleanup(r.activity.getClass().getName(), "Activity");

}

//2.切换线程

final void scheduleContextCleanup(...) {

sendMessage(H.CLEAN_UP_CONTEXT, cci);

}

//3.通知LoadedApk中的方法

final void performFinalCleanup(String who, String what) {

mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);

}

//4.removeContextRegistrations中取消注册

public void removeContextRegistrations(Context context,...) {

//找到context对应的所有接收者

ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap = mReceivers.remove(context);

//取消所有接收者的注册

for (int i = 0; i < rmap.size(); i++) {

ActivityManager.getService().unregisterReceiver(rd.getIIntentReceiver());

}

//移除客户端的注册

mUnregisteredReceivers.remove(context);

}

1.Activity在destory的时候,会调用scheduleFinalCleanup方法;

2.这个方法中,会通过scheduleContextCleanup方法切换到主线程中执行;

3.主线程中调到LoadedApk中的removeContextRegistrations方法;

4.removeContextRegistrations中,首先找到Activity中所注册的所有接收者并且移除注册,然后依次通知AMS取消注册,最后从取消注册的map中,也删掉接收者。

八.总结

讲了这么多,每个章节也都包涵很多知识点,所以没有点储备,有可能很难理解。所以最后来做一个总结:

首先,我们按照注册方式的不同,分成动态广播和静态广播。动态广播最终会把广播接收者注册到IntentResolver中,而静态广播没有注册流程,只需要在manifest中声明即可。使用时PackageManager会遍历所有APP的manifest进行查找。

然后,我们在按照发送方式的不同,分成无序广播和有序广播,其中静态广播都属于有序广播类型。无序广播,是发送方发送广播后,所有接收者并行接收的。有序广播,是发送方发送广播后,依次接收到广播事件,优先级高的先收到,甚至可以在接收者中结束掉当前广播流程。

有序广播中,我们在分出来一种特殊类型就是静态广播。如果接收者进程存在,则直接发送给对应的进程,如果进程不存在,则首先会创建APP进程,在APP创建完成通知系统侧后,在进行广播事件的发送。

最后,用一张图来总结一下,方便记录和理解。

————————————————

版权声明:本文为CSDN博主「失落夏天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/rzleilei/article/details/128550566

android四大组件之四-BroadCast实现原理分析相关推荐

  1. android 广播解绑,Android四大组件之BroadCast

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? Android四大组件之BroadCast 一.广播概述 Android应用可以从Android系统和其他Android ...

  2. Android四大组件之——Broadcast学习总结

    1.Broadcast概念 是一个全局的监听器,属于Android四大组件之一.Broadcast(广播)是一种广泛运用的在应用程序(APP)之间传输信息的机制.而BroadcastReceiver( ...

  3. Android四大组件之broadcast广播使用整理笔记

    一.广播机制概述 通常情况下在学校的每个教室都会装有一个喇叭,这些喇叭是接入到学校广播室的.如果有重要通知,会发送一条广播来告知全校师生.为了便于发送和接收系统级别的消息通知,Android系统也引入 ...

  4. Android四大组件之一 --Broadcast(广播通信)

    一,Broadcast简介 Broad cast 广播是广泛应用在应用程序之间传输信息的机制,而Broadcast Receiver是对发送出来的广播进行过滤接收并响应的一类组件 二,Broadcas ...

  5. Android四大组件之BroadCast(续)

    1.哪一个方法可以发送广播? activity.sendbroadcast or context.sentbroadcast or service.sendbroadcast 2.创建广播接受程序必须 ...

  6. Android四大组件系列7 Broadcast广播机制(上)

    一 概述 广播 (Broadcast) 机制用于进程或线程间通信,广播分为广播发送和广播接收两个过程,其中广播接收者 BroadcastReceiver 是 Android 四大组件之一.Broadc ...

  7. Android四大组件易考题分析

    题型1:请列出Android四大组件: 答:Activity,Service,BroadcastReceiver,Provider 题型2:请描述下Activity的生命周期 答:7个生命周期 onC ...

  8. Android四大组件之bindService源码实现详解

        Android四大组件之bindService源码实现详解 Android四大组件源码实现详解系列博客目录: Android应用进程创建流程大揭秘 Android四大组件之bindServic ...

  9. Android四大组件总结

    四大组件系列文章 Activity总结和补充:Activity总结和补充_fdsafwagdagadg6576的博客-CSDN博客 Android Binder&Aidl理解和补充:Andro ...

最新文章

  1. 实现ASP.NET MVC3 HtmlHelper 的 RadioButtonList 与CheckBoxList 扩展
  2. Android 开源框架Universal-Image-Loader全然解析(一)--- 基本介绍及使用
  3. mysql gbk支持_mysql如何支持gbk编码
  4. 拓扑排序(Topology_Sort)
  5. nginx源码阅读(一).综述
  6. Eclipse构建Maven分包分模块项目并构建服务端
  7. gzip,bzip2
  8. go test遇到的一些问题-command-line-arguments undefined: xxxxx
  9. 百练_4123 马走日(深搜)
  10. 三菱PLC学习方法分享
  11. php防止sql注入的方法
  12. java导出格式与拓展名不一致_ASP.NET导出Excel打开时提示:与文件扩展名指定文件不一致解决方法...
  13. 环境变量(PATH)的那些事
  14. Win11输入法的选字框不见了怎么办?
  15. 屏的像素与传输速率_HDMI线的传输速率是如何定义的
  16. Synchronized锁升级底层原理
  17. 征途手机版服务器维护,征途手机版手游开服表_征途手机版手游开服时间表_新服新区预告_第一手游网...
  18. yandex广告的投放要求 (网站篇)
  19. php图片批量上传插件下载,jQuery的多图片批量上传插件
  20. 《鸿图之下》中的武将是如何做美术设计的?

热门文章

  1. 2021-2027全球与中国气溶胶采样器市场现状及未来发展趋势
  2. STM32 控制4位共阳数码管输出计数
  3. android测试之getevent/sendevent
  4. Apple 公司账号续费流程
  5. 02 区块链的安全和隐私
  6. 兼职币圈代言人?特斯拉CEO:加密货币是我的安全词
  7. 人工智能AI、机器学习模型理解
  8. windows 10 defender 577的错误处理方法
  9. android开发笔记之Google Analytics
  10. ERP术语解释及SAP模块介绍