通过本文,我们可以掌握:
- Broadcast的ANR触发机制
- Service的ANR触发机制
- ContentProvider的ANR触发机制
当系统发生ANR时,通常它会蹦出提示弹框 —> 应用程序无响应。此时你有两种选择:
1、关闭应用;
2、继续等待。
这其实是系统在告诉我们:啊,当前APP出问题了,咱暂时没法响应后续操作了哦!
那么这背后,系统是怎么知道后续操作没法执行了呢?
系统咋知道ANR场景?
其实,我们在操作APP,主要都是在四大组件上进行各种操作。所以系统也主要监听四大组件触发ANR的情况。
作为一套系统机制,最好不要对单一成员有特殊对待,而采用不一样的监听机制。
所以,四大组件在触发ANR时,都坚持“埋雷 -> 拆雷 -> 暴雷”的原则。
那么我们分别来看看四大组件是怎么坚持这个统一原则的。
以下分析代码从官网获取:platform_framework_base
Broadcast
启动流程
广播分为:有序广播、无序广播、本地广播。其中,只有有序广播坚持了统一原则,所以我们的重点在有序广播。
所以我们从sendOrderedBroadcast() 入口开始,梳理整个广播执行的生命周期流程:

埋雷、拆雷、爆雷
首先,调用方经ContextImpl中转到系统进程的ActivityManagerService(AMS) 服务;
@Override
public void sendBroadcast(Intent intent) {
......
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntentWithFeature(
mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
null, Activity.RESULT_OK, null, null, null, null /*excludedPermissions=*/,
null, AppOpsManager.OP_NONE, null, false, false, getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
经过一轮各种权限校验(如:userId、action等)后,交给广播队列BroadcastQueue(这里会根据不同的flag,选择是前台广播队列,还是后台广播队列。flag是根据调用方的发送方式加入到Intent中的)
可以看到,前台广播队列和后台广播队列其实都是BroadcastQueue,但构造参数实参略有不同
- name:广播队列名
- constants:对应了超时时长:


因此才有了前台广播10s超时,后台广播60s超时。但这是单个广播的超时时长,如果是整个BroadcastQueue的话,则是2*receiver个数*timeout时长为超时时长。
- allowDelayBehindServices:前台广播是false,后台广播是true:
/**
* If true, we can delay broadcasts while waiting services to finish in the previous
* receiver's process.
*/
final boolean mDelayBehindServices;
根据代码注释猜测:后台广播的话,若在前一个广播接收器进程中,有服务service需要停止的话,当前广播可以延迟执行。(一般咱好像也用不到,先忽略吧😛)
然后,BroadcastQueue收到处理广播消息后,通过Handler发送出去,进行排队等待处理(此处的Handler所在线程是AMS);
由processNextBroadcastLocked真正处理广播。首先处理无序广播,循环发送给广播接收者;
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
//性能监控
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
System.identityHashCode(r));
}
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
//循环遍历发送给广播接收者
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
}
接着就开始处理有序广播。关键的来了!首先若当前还有广播接收者,且当前时间大于整个广播队列的超时时长了的话,就会发送超时消息,强制停止该广播(根据代码注释了解到,这类广播是由于系统超时为及时响应所以还存在,这部分被挂起的广播此时就需要立刻停止)。
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && r.dispatchTime > 0) {
long now = SystemClock.uptimeMillis();
//当前广播还有接收者
if ((numReceivers > 0) &&
//当前时间大于整个BroadcastQueue的超时时长
(now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
Slog.w(TAG, "Hung broadcast ["
+ mQueueName + "] discarded after timeout failure:"
+ " now=" + now
+ " dispatchTime=" + r.dispatchTime
+ " startTime=" + r.receiverTime
+ " intent=" + r.intent
+ " numReceivers=" + numReceivers
+ " nextReceiver=" + r.nextReceiver
+ " state=" + r.state);
//立刻发送超时消息,强制停止当前广播
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
咱此时的广播不属于这类,所以接着往下走:
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
if (r.resultTo != null) {
......
if (sendResult) {
.......
try {
if (DEBUG_BROADCAST) {
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ r.intent.getAction() + " app=" + r.callerApp);
}
if (r.dispatchTime == 0) {
// The dispatch time here could be 0, in case it's a parallel
// broadcast but it has a result receiver. Set it to now.
r.dispatchTime = now;
}
r.mIsReceiverAppRunning = true;
//1、发送消息给广播接收者
performReceiveLocked(r, r.resultToApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.shareIdentity,
r.userId, r.callingUid, r.callingUid, r.callerPackage,
r.dispatchTime - r.enqueueTime,
now - r.dispatchTime, 0,
r.resultToApp != null
? r.resultToApp.mState.getCurProcState()
: ActivityManager.PROCESS_STATE_UNKNOWN);
......
r.resultTo = null;
} catch (RemoteException e) {
r.resultTo = null;
Slog.w(TAG, "Failure ["
+ mQueueName + "] sending broadcast result of "
+ r.intent, e);
}
}
}
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
//2、拆雷
cancelBroadcastTimeoutLocked();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Finished with ordered broadcast " + r);
......
}
然后通过performReceiveLocked发送消息给广播接收者,去执行操作。执行完后再调用cancelBroadcastTimeoutLocked操作,该操作就是“拆雷”。这怎么就拆雷了?还没埋呢。。。
这其实是因为每次广播接收者处理完广播后,会通知AMS处理完毕,AMS再告诉BroadcastQueue可以处理下一个广播了,所以此时拆的雷是上一个广播埋下的雷。(有的地方会说埋雷是在第5步的位置,但其实条件并不满足)。具体埋雷还在下面。
// Get the next receiver...
int recIdx = r.nextReceiver++;
// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
r.receiverTime = SystemClock.uptimeMillis();
r.scheduledTime[recIdx] = r.receiverTime;
if (recIdx == 0) {
r.dispatchTime = r.receiverTime;
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
......
//性能监控
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
System.identityHashCode(r));
}
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
+ mQueueName + "] " + r);
}
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
//埋雷!
setBroadcastTimeoutLocked(timeoutTime);
}
当我们获取下一个广播接收器后,并且没有发送过超时消息,则通过setBroadcastTimeoutLocked设置超时消息,也即埋雷!
好,到这埋雷,拆雷都完成了。那么若没及时拆雷就会爆了,爆雷又是如何的呢?
爆雷
超时过后,最终会走到:
final void broadcastTimeoutLocked(boolean fromMsg) {
......
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastTimeoutLocked()");
try {
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
......
// The ANR should only be triggered if we have a process record (app is non-null)
if (!debugging && app != null) {
//交由AMS处理ANR事件
mService.appNotResponding(app, timeoutRecord);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
如上,爆雷后通知AMS处理ANR事件
☀️由此,有序广播完成了:埋雷 -> 拆雷 -> 暴雷的过程。
Service
启动流程
启动服务分为startService和bindService。最终其实都殊途同归,所以我们以startService为例:

埋雷、拆雷、爆雷
首先,同样是经过ContextImpl代理给系统进程的ActivityManagerService(AMS)处理。AMS也不会自己处理(毕竟作为大管家要管的事太多了),而是将这部分的职责交给ActiveServices处理。
由ActiveServices.startServiceLocked() 真正开始处理服务启动。
第一步国际惯例,确认调用方是否OK,将调用方封装成ServiceLookupResult实体,该实体内包含了对应的ServiceRecord记录,记载了Service调用方的元信息。
然后也是国际惯例,各种权限校验,检查,通知BatteryStatsService服务启动了(或许是功耗监控?先不管)。经bringUpServiceLocked执行拉起服务操作(这里会加上Trace监控)。
这里会有区分了,若Service所在进程已经存在了,则直接调用realStartServiceLocked()执行真正拉起服务操作;若Service所在进程还没启动,则通过AMS通知Zygote启动目标进程,目标进程启动后与AMS绑定再调用realStartServiceLocked() 往下走。
所以,无论如何接着realStartServiceLocked()继续。(该操作也是拉起服务的核心操作)
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
boolean enqueueOomAdj, @ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy)
throws RemoteException {
//客户端不在了,则抛异常退出
if (thread == null) {
throw new RemoteException();
}
if (DEBUG_MU)
Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ ", ProcessRecord.uid = " + app.uid);
r.setProcess(app, thread, pid, uidRecord);
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean skipOomAdj = (serviceBindingOomAdjPolicy
& SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CREATE) != 0;
final ProcessServiceRecord psr = app.mServices;
final boolean newService = psr.startService(r);
//埋雷!!
bumpServiceExecutingLocked(r, execInFg, "create",
OOM_ADJ_REASON_NONE /* use "none" to avoid extra oom adj */,
skipOomAdj /* skipTimeoutIfPossible */);
mAm.updateLruProcessLocked(app, false, null);
updateServiceForegroundLocked(psr, /* oomAdj= */ false);
......
boolean created = false;
try {
if (LOG_SERVICE_START_STOP) {
......
//日志记录Service启动
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, pid);
}
final int uid = r.appInfo.uid;
final String packageName = r.name.getPackageName();
final String serviceName = r.name.getClassName();
......
//通知APP进程,执行创建Service操作。thread就是客户端的ApplicationThread
thread.scheduleCreateService(r, r.serviceInfo,
null /* compatInfo (unused but need to keep method signature) */,
app.mState.getReportedProcState());
r.postNotification(false);
created = true;
} catch (DeadObjectException e) {
......
} finally {
......
}
......
requestServiceBindingsLocked(r, execInFg, serviceBindingOomAdjPolicy);
......
}
//bumpServiceExecutingLocked -> scheduleServiceTimeoutLocked执行埋雷
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
return;
}
//根据前台服务还是后台服务设置不同的delay时间
final long delay = proc.mServices.shouldExecServicesFg()
? mAm.mConstants.SERVICE_TIMEOUT : mAm.mConstants.SERVICE_BACKGROUND_TIMEOUT;
mActiveServiceAnrTimer.start(proc, delay);
proc.mServices.noteScheduleServiceTimeoutPending(false);
}
由此,可见在通知客户端进程创建Service之前,已经准备好了埋雷操作,并根据前台服务20s,后台服务200s 的要求设置爆雷延迟时间。
然后,进入到客户端进程。客户端进程的Binder收到接口调用:
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
//通过Handler将创建Service的任务加入到消息队列中
sendMessage(H.CREATE_SERVICE, s);
}
轮到创建Service执行时:
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
final LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo);
Service service = null;
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
Application app = packageInfo.makeApplicationInner(false, mInstrumentation);
final java.lang.ClassLoader cl;
//1、获取加载Service的类加载器
if (data.info.splitName != null) {
cl = packageInfo.getSplitClassLoader(data.info.splitName);
} else {
cl = packageInfo.getClassLoader();
}
//2、反射获取Service实例
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
//3、创建Service的上下文context
ContextImpl context = ContextImpl.getImpl(service
.createServiceBaseContext(this, packageInfo));
if (data.info.splitName != null) {
context = (ContextImpl) context.createContextForSplit(data.info.splitName);
}
if (data.info.attributionTags != null && data.info.attributionTags.length > 0) {
final String attributionTag = data.info.attributionTags[0];
context = (ContextImpl) context.createAttributionContext(attributionTag);
}
// Service resources must be initialized with the same loaders as the application
// context.
//4、Service的context的资源需要加载所在进程的resources
context.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
context.setOuterContext(service);
//5、Service对象绑定Context
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
......
//6、回调Servie.onCreate()
service.onCreate();
mServicesData.put(data.token, data);
mServices.put(data.token, service);
try {
//7、通知AMS,Service已经创建完毕
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
......
}
}
整个过程可概括为:
1、获取当前应用的类加载器;
2、通过类加载器反射获取Service对象;
3、创建Service的上下文context,其context添加App的resources;
4、Service对象和其context绑定;
5、回调Service.onCreate();
6、通知AMS,客户端的Service已经创建完毕。
此时,又回调system_server进程,AMS收到通知后转而叫告诉ActiveServices了(不是我的活,坚决不接😛)。
ActiveServices接过接力棒之后,最终在serviceDoneExecutingLocked进行拆雷(取消ANR定时器):
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing, boolean enqueueOomAdj, @OomAdjReason int oomAdjReason) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "<<< DONE EXECUTING " + r
+ ": nesting=" + r.executeNesting
+ ", inDestroying=" + inDestroying + ", app=" + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"<<< DONE EXECUTING " + r.shortInstanceName);
r.executeNesting--;
if (r.executeNesting <= 0) {
if (r.app != null) {
final ProcessServiceRecord psr = r.app.mServices;
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"Nesting at 0 of " + r.shortInstanceName);
psr.setExecServicesFg(false);
psr.stopExecutingService(r);
if (psr.numberOfExecutingServices() == 0) {
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortInstanceName);
//目标进程还在,则取消对应的ANR定时器
if (r.app.mPid != 0) mActiveServiceAnrTimer.cancel(r.app);
} else if (r.executeFg) {
// Need to re-evaluate whether the app still needs to be in the foreground.
for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
if (psr.getExecutingServiceAt(i).executeFg) {
psr.setExecServicesFg(true);
break;
}
}
}
......
}
......
}
}
有了,埋雷、拆雷,那爆雷呢?
当AnrTimer时间到了后,就会通过AMS的handler发送超时消息,通知AMS处理ANR操作。当然,AMS又丢给ActiveServies了。。。
void serviceTimeout(ProcessRecord proc) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout()");
TimeoutRecord timeoutRecord = null;
......
if (timeoutRecord != null) {
//交由AnrHelper处理ANR超时消息
mAm.mAnrHelper.appNotResponding(proc, timeoutRecord);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
☀️由此,Service完成了埋雷、拆雷、爆雷的操作
ContentProvider
ContentProvider的ANR,是不是感觉没咋遇到过?没错,我们一般使用的ContentProvider发生ANR时是不会弹ANR弹框,也不会dump相应的ANR日志的,它只会把发生ANR的进程给。。。干掉!
在我们的APP启动时,ContentProvider就会启动了,其埋雷也正在此时进行:
//ContentProvider和App绑定
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid); // 根据pid获取ProcessRecord
}
}
...
//系统处于ready状态或者该app为FLAG_PERSISTENT进程则为true
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List<ProviderInfo> providers = normalMode ?
generateApplicationProvidersLocked(app) : null;
//app进程存在正在启动中的provider,则超时10s后发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
thread.bindApplication(...);
...
}
没错,在ContentProvider启动时和App绑定过程中就通过Handler发送了超时消息了,也就是在这里埋了雷。
然后通过publish的方式告诉AMS处理事件,当然AMS又当了甩手掌柜,交给ContentProviderHelper处理。拆雷也正在此处进行:
/**
* List of content providers who have clients waiting for them. The
* application is currently being launched and the provider will be
* removed from this list once it is published.
*/
//粗略理解:mLaunchingProviders存储的是启动的ContentProvider。但当CP被发布(启动)后,就会被移除
private final ArrayList<ContentProviderRecord> mLaunchingProviders = new ArrayList<>();
void publishContentProviders(IApplicationThread caller
, List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
mService.enforceNotIsolatedOrSdkSandboxCaller("publishContentProviders");
synchronized (mService) {
final ProcessRecord r = mService.getRecordForAppLOSP(caller);
if (DEBUG_MU) {
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
}
if (r == null) {
throw new SecurityException("Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}
final long origId = Binder.clearCallingIdentity();
boolean providersPublished = false;
for (int i = 0, size = providers.size(); i < size; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.mProviders.getProvider(src.info.name);
if (dst == null) {
continue;
}
if (DEBUG_MU) {
Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
}
providersPublished = true;
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String[] names = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
boolean wasInLaunchingProviders = false;
for (int j = 0, numLaunching = mLaunchingProviders.size(); j < numLaunching; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
wasInLaunchingProviders = true;
j--;
numLaunching--;
}
}
//成功publish,则移除超时消息
if (wasInLaunchingProviders) {
mService.mHandler.removeMessages(
ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG
, dst);
mService.mHandler.removeMessages(
ActivityManagerService.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG
, r);
}
......
}
......
Binder.restoreCallingIdentity(origId);
}
}
publishContentProviders() 是在app启动后,发布ContentProvider时调用的方法,只有发布成功了,系统才能识别到,APP才能使用到。所以这整个过程是:在ContentProvider启动时埋雷,到发布成功后再拆雷。后续在CRUD或call就不涉及了。
那埋了雷,拆了雷。爆雷干了啥?
//AMS收到超时消息后,转交给ContentProviderHelper处理
void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
cleanupAppInLaunchingProvidersLocked(app, true);
mService.mProcessList.removeProcessLocked(app, false, true,
ApplicationExitInfo.REASON_INITIALIZATION_FAILURE,
ApplicationExitInfo.SUBREASON_UNKNOWN,
"timeout publishing content providers");
}
可以看到,爆雷后就是清除启动的进程,并从对应的缓存中移除。
以上,就是常见的3大组件的ANR触发机制原理了。
等下,说好的四大组件呢?怎么就只有3个?Are you kidding me???
其实我们在Activity主线程中遇到的ANR,通常会结合Input系统分析,通常是主线程阻塞然后喂了一个input事件后,就爆出了经典的Input Timeout了。(顺带一提,Activity的ANR埋雷在Activity切换流程中。只是我目前还没遇到过Activity本身暴雷的🤣,也未曾见过各路豪杰有相关的分享,或许确实极少遇到。若有大佬有相关文章,欢迎推荐~)
但input体系过于繁琐,其ANR逻辑可以单开一文了,先留个坑吧,后续补上!😂
822

被折叠的 条评论
为什么被折叠?



