《ANR系列 --- 触发机制》

通过本文,我们可以掌握:

  • 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逻辑可以单开一文了,先留个坑吧,后续补上!😂

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值