Android ANR触发机制(二)

news2024/9/24 15:25:17

上一篇文章看了Service的ANR触发流程,现在看一下其他三种ANR触发流程。

1.BroadcastReceiver触发ANR

BroadcastReceiver超时是位于ActivityManager线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。

广播队列分为foreground队列和background队列两个。其中前台广播的超时为10s;后台广播的超时为60s。

广播的使用需要注册广播和发送广播两步:

0ecf6a768a564009bcf8780ed57b1c1f.png

①注册广播

不管Activity还是Service中的registerReceiver,最终都调用ContextImpl中的registerReceiver方法:

ContextImpl.java:

@Override

public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler) {

    return registerReceiverInternal(receiver, getUserId(), filter, broadcastPermission, scheduler, getOuterContext(), 0);

}

这个方法调用registerReceiverInternal方法:

private Intent registerReceiverInternal( BroadcastReceiver receiver, int userId, IntentFilter filter, String broadcastPermission, Handler scheduler, Context context, int flags) {

    ……

    final Intent intent = ActivityManager.getService().registerReceiver( mMainThread.getApplicationThread(), mBasePackageName, rd, filter, broadcastPermission, userId, flags);

    …

}

这里调用了AMS的registerReceiver方法:

AMS.java:

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId, int flags) {

        mRegisteredReceivers.put( receiver.asBinder(), rl); //在这里将信息保存

    }

}

可见注册广播就是通过层层调用,将BroadcastReceiver信息保存到AMS中。

②发送广播

在ContextImpl中调用sendBroadcast方法:

ContextImpl.java:

@Override

public void sendBroadcast(Intent intent) {

    ActivityManager.getService().broadcastIntent( mMainThread.getApplicationThread(), intent, resolvedType, null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false, getUserId());

}

发送广播调用了AMS的broadcastIntent方法:

AMS.java:

public final int broadcastIntent( IApplicationThread caller, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean serialized, boolean sticky, int userId) {

    int res = broadcastIntentLocked(callerApp, callerApp != null ? callerApp.info.packageName : null, intent, resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky, callingPid, callingUid, userId);

}

这个方法又调用broadcastIntentLocked方法:

final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions, boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {

    //...

    queue.scheduleBroadcastsLocked();

    //...

}

在broadcastIntentLocked方法中,通过调用BroadcastQueue的scheduleBroadcastsLocked方法发送了一个BROADCAST_INTENT_MSG消息:

public void scheduleBroadcastsLocked() {

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

}

通过BroadcastHandler发送了一个BROADCAST_INTENT_MSG消息。收到该消息后,会在handleMessage方法中进行处理:

BroadcastQueue.java:

private final class BroadcastHandler extends Handler {

    @Override

    public void handleMessage(Message msg) {

        switch (msg.what) {

            case BROADCAST_INTENT_MSG: 

                processNextBroadcast(true);

                 break;

        }

    }

}

在handleMessage方法中调用processNextBroadcast方法:

final void processNextBroadcast(boolean fromMsg) {

    synchronized (mService) {

        processNextBroadcastLocked(fromMsg, false);

    }

}

调用processNextBroadcastLocked方法:

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {

    //(装炸弹)设置超时时间

     setBroadcastTimeoutLocked(timeoutTime);

    //回调onReceive()方法,如果在延迟时间内没有执行完成,则会触发ANR(引爆炸弹)

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

    //(拆炸弹)onReceive在规定时间内执行完成,则取消延迟消息

    cancelBroadcastTimeoutLocked(); 

}

发送广播时,调用的这三个方法很重要。其中setBroadcastTimeoutLocked方法设置超时时间,也就是装炸弹。deliverToRegisteredReceiverLocked内会回调onReceive()方法,如果在延迟时间内没有执行完成,则会触发ANR,也就是引爆炸弹。cancelBroadcastTimeoutLocked()用于取消延迟消息,也就是拆炸弹。

下面分别看一下:

①装炸弹

final void setBroadcastTimeoutLocked(long timeoutTime) {

    Message msg = mHandler.obtainMessage( BROADCAST_TIMEOUT_MSG, this);

     mHandler.sendMessageAtTime(msg, timeoutTime);

}

static final int BROADCAST_FG_TIMEOUT = 10*1000;

static final int BROADCAST_BG_TIMEOUT = 60*1000;

发送广播的时候,同时发送了一个BROADCAST_TIMEOUT_MSG延迟消息(炸弹)。

②引爆炸弹

private void deliverToRegisteredReceiverLocked( BroadcastRecord r, BroadcastFilter filter, boolean ordered, int index) {

    performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky, r.userId);

    ...

}

这个方法会调用performReceiveLocked方法:

BroadcastQueue.java:

void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) throws RemoteException {

    …

    app.thread.scheduleRegisteredReceiver( receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.repProcState);

}

调用Activity中的ApplicationThread的scheduleRegisteredReceiver方法:

ActivityThread$ApplicationThread.java:

public void scheduleRegisteredReceiver( IIntentReceiver receiver, Intent intent, int resultCode, String dataStr, Bundle extras, boolean ordered, boolean sticky, int sendingUser, int processState) throws RemoteException {

    …

    receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky, sendingUser);

}

调用BroadcastQueue的ReceiverDispatcher的InnerReceiver的performReceive方法:

@Override

public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {

    final LoadedApk.ReceiverDispatcher rd;

    rd.performReceive(intent, resultCode, data, extras, ordered, sticky, sendingUser);

}

调用BroadcastQueue的ReceiverDispatcher的performReceive方法:

public void performReceive(Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser) {

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

   …

}

这里调用了ReceiverDispatcher的Handler类型的mActivityThread的post方法,将一个runnable发送出去。

public final Runnable getRunnable() {

    return () -> {

        //回调onReceive

        receiver.onReceive(mContext, intent);

        …

    }

}

在这里回调onReceive。

③拆炸弹

onReceive执行完成后,会调用cancelBroadcastTimeoutLocked方法取消延迟消息,也就是拆炸弹。

BroadcastQueue.java:

final void cancelBroadcastTimeoutLocked() {

    mHandler.removeMessages( BROADCAST_TIMEOUT_MSG, this);

}

此时移除了超时检测的消息,此时ANR不会触发。

如果没有在规定时间完成,则会处理BROADCAST_TIMEOUT_MSG消息:

@Override

public void handleMessage(Message msg) {

    switch (msg.what) {

        case BROADCAST_TIMEOUT_MSG: 

            synchronized (mService) {

                broadcastTimeoutLocked(true);

            }

            break;

    }

}

调用broadcastTimeoutLocked触发ANR:

final void broadcastTimeoutLocked(boolean fromMsg) {

     ...

    mHandler.post(new AppNotResponding(app, anrMessage));

}

总结一下BroadcastReceiver的ANR触发流程:

①发送消息时,会通过Handler发送一个BROADCAST_TIMEOUT_MSG延迟消息(装炸弹)。

②如果onReceive方法在延迟时间内执行完成,则取消那个延迟消息(拆炸弹)。

③否则,执行Handler的handMessage方法来处理超时消息(引爆炸弹)。

 

2.ContentProvider触发ANR

ContentProvider超时是位于ActivityManager线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。

ContentProvider超时时间是10s。

1)第一步:装炸弹

ContentProvider的注册在启动进程的时候就开始执行,进程创建后如果有ContentProvider就会进入AMS进程调用attachApplicationLocked()方法:

ActivityManagerService.java:

private final boolean attachApplicationLocked( IApplicationThread thread, int pid) {

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

    }

}

static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000; //10s

在绑定Application时,会判断是否有ContentProvide,如果有,则会通过Handler发送CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG延迟消息(炸弹)。

2)第二步:拆炸弹

在AT.installContentProviders()安装完后会调用AMS.publishContentProviders()方法,即当provider成功publish之后,便会拆除该炸弹:

public final void publishContentProviders( IApplicationThread caller, List<ContentProviderHolder> providers) {

    //成功pubish则移除该消息

   if (wasInLaunchingProviders) {

       mHandler.removeMessages(CONTENT_PR OVIDER_PUBLISH_TIMEOUT_MSG, r);

   }    

}  

3)第三步:引爆炸弹

如果延迟消息在指定时间内没被移除则引爆炸弹。CONTENT_PROVIDER_PUBLISH_TIMEOUT _MSG的handler在AMS.MainHandler中,当倒计时结束便会向该Handler线程发送延迟信息,MainHandler是AMS的内部类。 

final class MainHandler extends Handler {

    public void handleMessage(Message msg) {

        switch (msg.what) {

            case CONTENT_PROVIDER_PUBLISH_TIM EOUT_MSG: 

                ProcessRecord app = (ProcessRecord)msg.obj;

                synchronized ( ActivityManagerService.this) {

                   processContentProviderPublishTime dOutLocked(app);

                }

            break;

        }

    }

}

private final void processContentProviderPub lishTimedOutLocked(ProcessRecord app) {

    cleanupAppInLaunchingProvidersLocked(app, true); //移除死亡的provider

    mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers"); //移除mProcessList中的相应对象

}

总结一下ContentProvider的ANR触发流程:

①app进程启动的时候,如果有ContentProvider就会注册ContentProvider,在注册过程中发送一个延迟消息(炸弹)。

②如果在延迟消息内ContentProvider成功pubish了,则移除该延迟消息(拆炸弹)。

③否则会引爆炸弹,触发ANR。

 

3.InputDispatching超时机制

input的超时检测机制跟service、broadcast、provider截然不同,为了更好的理解input过程先了解两个重要线程的相关工作:

①InputReader线程负责通过EventHub(监听目录/dev/input)读取输入事件,一旦监听到输入事件则放入到InputDispatcher的mInBoundQueue队列,并通知其处理该事件;

②InputDispatcher线程负责将接收到的输入事件分发给目标应用窗口,分发过程使用到3个事件队列:mInBoundQueue用于记录InputReader发送过来的输入事件;outBoundQueue用于记录即将分发给目标应用窗口的输入事件;waitQueue用于记录已分发给目标应用,且应用尚未处理完成的输入事件。

所以,InputReader不断的从EventHub中监听是否有Input事件,InputReader把事件分发给InputDispatcher。InputDispatcher调用dispatchOnce()方法开始把事件分发给对应的View,就从InputDispatcher的分发开始监控ANR,InputDispatcher的ANR区间是查找窗口findFocusedWindowTargetsLocked()方法到resetANRTimeoutsLocked()重置方法。

void InputDispatcher::dispatchOnce() {

    ...

    // 调用dispatchOnceInnerLocked

    dispatchOnceInnerLocked( &nextWakeupTime);

}

void InputDispatcher::dispatchOnceInnerLocked( nsecs_t* nextWakeupTime) {

    nsecs_t currentTime = now();

     ...  

    resetANRTimeoutsLocked();  // 重置标记

    switch (mPendingEvent -> type) {

        case EventEntry::TYPE_KEY: 

            // key类型

            done = dispatchKeyLocked(currentTime, typedEntry, & dropReason, nextWakeupTime);

            break;

        case EventEntry::TYPE_MOTION: 

            done = dispatchMotionLocked( currentTime, typedEntry, & dropReason, nextWakeupTime);

            break;

        default:

            ALOG_ASSERT(false);

            break;

    }

}

void InputDispatcher::resetANRTimeoutsLocked( ) {

    // 将mInputTargetWaitCause设置为INPUT_TARGET_WAIT_CAUSE_NONE

    mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE;

    mInputTargetWaitApplicationToken.clear();

}

在分发之前会调用resetANRTimeoutsLocked()方法,重置mInputTargetWaitCause标记为:INPUT_TARGET_WAIT_CAUSE_NONE。接着根据下发的类型,寻找对应的窗口,比如KEY类型,则调用dispatchKeyLocked()方法。

bool InputDispatcher::dispatchKeyLocked( nsecs_t currentTime, KeyEntry*entry, DropReason*dropReason, nsecs_t*nextWakeupTime) {

    // 寻找目标窗口

    int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime);

    // 给目标窗口分发事件

    dispatchEventLocked(currentTime, entry, inputTargets);

    return true;

}

int32_t InputDispatcher::findFocusedWindowTar getsLocked(nsecs_t currentTime, const EventEntry*entry, std::vector<InputTarget>&inputTargets, nsecs_t*nextWakeupTime) {

    ...

    // 检查窗口不能input的原因

    reason = checkWindowReadyForMoreInputLo cked(currentTime, focusedWindowHandle, entry, "focused");

    if (!reason.empty()) {

        // 调用handleTargetsNotReadyLocked()方法

        injectionResult = handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, focusedWindowHandle, nextWakeupTime, reason.c_str());

         goto Unresponsive;

    }

    ...

    return injectionResult;

}

int32_t InputDispatcher::handleTargetsNotRea dyLocked(nsecs_t currentTime,const EventEntry*entry,const sp<InputApplicationHandle>&applicationHandle,const sp<InputWindowHandle>&windowHandle, nsecs_t*nextWakeupTime, const char*reason) {

    // 在resetANRTimeoutsLocked方法中,mInputTargetWaitCause为INPUT_TARGET_WAIT_CAUSE_NONE

    if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {

        // DEFAULT_INPUT_DISPATCHING_TIMEOUT为5s

        nsecs_t timeout;

        if (windowHandle != nullptr) {

            timeout = windowHandle -> getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);

        } else if (applicationHandle != nullptr) {

            timeout = applicationHandle -> getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);

        } else {

            timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;

        }

        // 要等到下次调用resetANRTimeoutsLocked时才能进

        mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;

        // 当前时间加上5s

        mInputTargetWaitTimeoutTime = currentTime + timeout;

        mInputTargetWaitTimeoutExpired = false;

        mInputTargetWaitApplicationToken.clear();

    }

    if (mInputTargetWaitTimeoutExpired) {

        return INPUT_EVENT_INJECTION_TIMED_OUT;

    }

    if (currentTime >= mInputTargetWaitTimeoutTime) {

        // 当前时间超过设定的5s,后执行onANRLocked()的ANR方法

        onANRLocked(currentTime, applicationHandle, windowHandle, entry -> eventTime, mInputTargetWaitStartTime, reason);

        return INPUT_EVENT_INJECTION_PENDING;

    } else {

        // Force poll loop to wake up when timeout is due.

        if (mInputTargetWaitTimeoutTime < *nextWakeupTime){

             *nextWakeupTime = mInputTargetWaitTimeoutTime;

        }

        return INPUT_EVENT_INJECTION_PENDING;

    }

}

在分发一次事件时,会调用resetANRTimeoutsLocked将标记为INPUT_TARGET_WAIT_CAUSE_NONE,所以第一次事件会设置一个5s后的超时时间,并把标记设置为INPUT_TARGET_WAIT_CAUSE_APPLICATI ON_NOT_READY,如果下次事件来临时当前的时间超过上次设置的5s时间就会调用onANRLocked()方法产生ANR。

void InputDispatcher::onANRLocked(nsecs_t currentTime, const sp<InputApplicationHandle>&applicationHandle, const sp<InputWindowHandle>&windowHandle, nsecs_t eventTime, nsecs_t waitStartTime, const char*reason) {

    float dispatchLatency = (currentTime - eventTime) * 0.000001f;

    float waitDuration = (currentTime - waitStartTime) * 0.000001f;

    // 收集ANR现场信息

    time_t t = time(nullptr);

    struct tm tm;

    localtime_r( & t, &tm);

    char timestr[ 64];

    strftime(timestr, sizeof(timestr), "%F %T", & tm);

    mLastANRState.clear();

    mLastANRState += INDENT "ANR:\n";

    mLastANRState += StringPrintf( INDENT2"Time: %s\n", timestr);

    mLastANRState += StringPrintf( INDENT2"Window: %s\n", getApplicationWindowLabel(applicationHandle, windowHandle).c_str());

    mLastANRState += StringPrintf( INDENT2"DispatchLatency: %0.1fms\n", dispatchLatency);

    mLastANRState += StringPrintf( INDENT2"WaitDuration: %0.1fms\n", waitDuration);

    mLastANRState += StringPrintf( INDENT2"Reason: %s\n", reason);

    //dump信息

    dumpDispatchStateLocked(mLastANRState);

    //将ANR命令加入commandQueue

    CommandEntry * commandEntry = postCommandLocked( &InputDispatcher::doNotifyANRLockedInterruptible);

    commandEntry -> inputApplicationHandle = applicationHandle;

    commandEntry -> inputChannel = windowHandle != nullptr ? getInputChannelLocked(windowHandle -> getToken()) : nullptr;

    commandEntry -> reason = reason;

}

在下次执行InputDispatcher.dispatchOnce时会先执行commandQueue的队列命令,这里把InputDispatcher::doNotifyANRLockedInterruptible放入到了队列。

void InputDispatcher::doNotifyANRLockedInterr uptible(CommandEntry*commandEntry) {

    mLock.unlock();

    //mPolicy是指NativeInputManager

    nsecs_t newTimeout = mPolicy -> notifyANR( commandEntry -> inputApplicationHandle, commandEntry -> inputChannel ? commandEntry -> inputChannel -> getToken() : nullptr, commandEntry -> reason);

    mLock.lock();

    resumeAfterTargetsNotReadyTimeoutLocked( newTimeout, commandEntry -> inputChannel);

}

mPolicy -> notifyANR通过JNI最终调用到InputManagerService.notifyANR()方法:

private long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) {

    return mWindowManagerCallbacks.notifyANR( inputApplicationHandle, token, reason);

}

这里的mWindowManagerCallbacks是InputManagerCallback对象。

InputManagerCallback.java:

public long notifyANR(InputApplicationHandle inputApplicationHandle, IBinder token, String reason) {

    final long startTime = SystemClock.uptimeMillis();

    try {

        return notifyANRInner( inputApplicationHandle, token, reason);

    } finally {

    }

}

private long notifyANRInner( InputApplicationHandle inputApplicationHandle, IBinder token, String reason) {

    ...

    // 调用AMS的inputDispatchingTimedOut()方法

    long timeout = mService.mAmInternal.inputDispatchingTimedOut(windowPid, aboveSystem, reason);

    return 0; // abort dispatching

}

最终调用到AMS.inputDispatchingTimedOut()方法:

AMS.java:

long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {

    if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {

        throw new SecurityException("Requires permission " + FILTER_EVENTS);

    }

    ProcessRecord proc;

    long timeout;

    synchronized (this) {

        synchronized (mPidsSelfLocked) {

            proc = mPidsSelfLocked.get(pid);

        }

        timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;

    }

    // 调用inputDispatchingTimedOut

    if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {

        return -1;

    }

    return timeout;

}

boolean inputDispatchingTimedOut( ProcessRecord proc, String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String reason) {

    // 调用appNotResponding方法

    mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo, parentShortComponentName, parentProcess, aboveSystem, annotation);

    return true;

}

最终还是执行appNotResponding()方法。

 

注意,input的超时机制并非时间到了一定就会爆炸,而是在处理后续上报事件的过程才会去检测是否该爆炸,它更像扫雷的过程,具体如图:

71127187c34f4f3893109519fe804699.png

①InputReader线程通过EventHub监听底层上报的输入事件,一旦收到输入事件则将其放至mInBoundQueue队列,并唤醒InputDispatcher线程;

②inputDispatcher开始分发输入事件,设置埋雷的起点时间。

先检测是否有正在处理的事件(mPendingEvent),如果没有则取出mInBoundQueue队头的事件,并将其赋值给mPendingEvent,且重置ANR的timeout;否则不会从mInBoundQueue中取出事件,也不会重置timeout。

然后检查窗口是否就绪(checkWindowReadyForMoreInputLocked),满足以下任一情况:

1)对于按键类型的输入事件,则outboundQueue或者waitQueue不为空;

2)对于非按键的输入事件,则waitQueue不为空,且等待队头时间超时500ms。

就会进入扫雷状态(检测前一个正在处理的事件是否超时),终止本轮事件分发,否则继续执行步骤3。当应用窗口准备就绪,则将mPendingEvent转移到outBoundQueue队列。

③当outBoundQueue不为空,且应用管道对端连接状态正常,则将数据从outboundQueue中取出事件,放入waitQueue队列;

④InputDispatcher通过socket告知目标应用所在进程可以准备开始干活;

⑤App在初始化时默认已创建跟中控系统双向通信的socketpair,此时App的包工头(main线程)收到输入事件后,会层层转发到目标窗口来处理;

⑥包工头完成工作后,会通过socket向中控系统汇报工作完成,则中控系统会将该事件从waitQueue队列中移除。

input超时机制为什么是扫雷,而非定时爆炸呢?这是因为对于input来说即便某次事件执行时间超过timeout时长,只要用户后续没有再生成输入事件,则不会触发ANR。 这里的扫雷是指当前输入系统中正在处理着某个耗时事件的前提下,后续的每一次input事件都会检测前一个正在处理的事件是否超时(进入扫雷状态),检测当前的时间距离上次输入事件分发时间点是否超过timeout时长。如果前一个输入事件,则会重置ANR的timeout,从而不会爆炸。

到这里,关于service ,广播,provider的anr原因都清楚了。下面就看看是如何对anr信息进行收集的。

 

4.appNotResponding处理流程

不管是哪种anr,最终都会调用到ProcessRecord 的appNotResponding方法:

ProcessRecord.java:

void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo, String parentShortComponentName, WindowProcessController parentProcess, boolean aboveSystem, String annotation) {

    ArrayList<Integer> firstPids = new ArrayList<>(5);

    SparseArray<Boolean> lastPids = new SparseArray<>(20);

    mWindowProcessController.appEarlyNotResp onding(annotation, () -> kill("anr", true));

    // anr时间,实际上发生anr的时候,此时收集的运行堆栈有可能并不是引起 anr 的堆栈

    long anrTime = SystemClock.uptimeMillis();

    if (isMonitorCpuUsage()) {

        mService.updateCpuStatsNow();

    }

    synchronized (mService) {

        // 记录 anr 到 eventlog

        EventLog.writeEvent( EventLogTags.AM_ANR, userId, pid, processName, info.flags, annotation);

        }

    }

    // 记录 anr 到 mainlog 

    StringBuilder info = new StringBuilder();

    info.setLength(0);

    info.append("ANR in ").append(processName);

    if (activityShortComponentName != null) {

        info.append(" (").append( activityShortComponentName).append(")");

    }

    info.append("\n");

    info.append("PID: ").append(pid).append("\n");

    if (annotation != null) {

        info.append("Reason: ").append( annotation).append("\n");

    }

    if (parentShortComponentName != null && parentShortComponentName.equals(activityShortComponentName)) {

        info.append("Parent: ").append( parentShortComponentName).append("\n");

    }

 // 创建 cpu tracker 对象

    ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);

    // 收集堆栈信息

    File tracesFile = ActivityManagerService. dumpStackTraces(firstPids, (isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids, nativePids);

    String cpuInfo = null;

 // 添加 cpu 信息

    if (isMonitorCpuUsage()) {

        mService.updateCpuStatsNow();

        synchronized ( mService.mProcessCpuTracker) {

            cpuInfo = mService.mProcessCpuTr acker.printCurrentState(anrTime);

        }

        info.append( processCpuTracker.printCurrentLoad());

        info.append(cpuInfo);

    }

    info.append( processCpuTracker.printCurrentState(anrTime));

    }

    StatsLog.write(StatsLog.ANR_OCCURRED, uid, processName, activityShortComponentName == null ? "unknown": activityShortComponentName, annotation, (this.info != null) ? (this.info.isInstantApp() ? StatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE : StatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE) : StatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE, isInterestingToUserLocked() ? StatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND : StatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND, getProcessClassEnum(),(this.info != null) ? this.info.packageName : "");

    final ProcessRecord parentPr = parentProcess != null ? (ProcessRecord) parentProcess.mOwner : null;

    // 将traces文件 和 CPU使用率信息保存到dropbox,即data/system/dropbox目录

    mService.addErrorToDropBox("anr", this, processName, activityShortComponentName, parentShortComponentName, parentPr, annotation, cpuInfo, tracesFile, null);

    if(mWindowProcessController.appNotRe sponding(info.toString(), () -> kill("anr", true),() -> {

        synchronized (mService) {

            mService.mServices.scheduleServiceTime outLocked(this);

        }

    })) {

        return;

    }

    makeAppNotRespondingLocked( activityShortComponentName, annotation != null ? "ANR " + annotation : "ANR", info.toString());

    if (mService.mUiHandler != null) {

        Message msg = Message.obtain();

        msg.what = ActivityManagerService.SHO W_NOT_RESPONDING_UI_MSG;

        msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem);

   // 发送 anr 弹窗信息

        mService.mUiHandler.sendMessage( msg);

    }

}

当发生ANR时, 会按顺序依次执行:

①输出ANR Reason信息到EventLog. 也就是说ANR触发的时间点最接近的就是EventLog中输出的am_anr信息;

②收集并输出重要进程列表中的各个线程的traces信息,该方法较耗时; 

③输出当前各个进程的CPU使用情况以及CPU负载情况;

④将traces文件和 CPU使用情况信息保存到dropbox,即data/system/dropbox目录

⑤根据进程类型,来决定直接后台杀掉,还是弹框告知用户。 

 

5.总结

①当出现ANR时,都是调用到AMS.appNotResponding()方法, rovider例外.

②Timeout时长

对于前台服务,则超时为SERVICE_TIMEOUT = 20s;

对于后台服务,则超时为SERVICE_BACKGROUND_TIMEOUT = 200s

对于前台广播,则超时为BROADCAST_FG_TIMEOUT = 10s;

对于后台广播,则超时为BROADCAST_BG_TIMEOUT = 60s;

ContentProvider超时为CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10s;

③超时检测

Service超时检测机制:

超过一定时间没有执行完相应操作来触发移除延时消息,则会触发anr;

BroadcastReceiver超时检测机制:

有序广播的总执行时间超过 2* receiver个数 * timeout时长,则会触发anr;

有序广播的某一个receiver执行过程超过 timeout时长,则会触发anr;

④对于Service, Broadcast, Input发生ANR之后,最终都会调用AMS.appNotResponding;

对于provider,在其进程启动时publish过程可能会出现ANR, 则会直接杀进程以及清理相应信息,而不会弹出ANR的对话框. appNotRespondingViaProvider()过程会走appNotResponding(), 这个就不介绍了,很少使用,由用户自定义超时时间. 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/187748.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java+SpringBoot+Vue前后端分离学生管理系统设计与实现

博主介绍&#xff1a;✌全网粉丝3W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战✌ 博主作品&#xff1a;《微服务实战》专栏是本人的实战经验总结&#xff0c;《Spring家族及…

2023年二月份图形化二级打卡试题

活动时间 从2023年 2月1日至1月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; &#xff08;1&#xff09;小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 &#xff08;2&#xff09;小朋友做完题目后&…

数组中和为0的三个数

给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意: 答案中不可以包含重复的三元组。 示例 1: 输入: num…

了解SLI、SLO和SLA

了解SLI、SLO和SLA 概念解释 服务水平指标(SLI) SLI代表目前服务的状态&#xff0c;例如可以是最基本的接口成功率、p99响应时间&#xff0c;也可以是一些业务指标&#xff0c;例如用户投诉率之类的。是可量化&#xff0c;是可确定的。 服务水平目标(SLO) SLO是目标&#x…

【树】哈夫曼树和哈夫曼编码

哈夫曼&#xff08;Huffman&#xff09;树&#xff0c;又称最优树&#xff0c;是一类带权路径长度最短的树。最优二叉树&#xff08;哈夫曼树&#xff09;路径&#xff1a;从树中一个结点到另一个结点之间的分支构成这两个结点之间的路。路径长度:路径上的分支数目&#xff1b;…

mysql分组排序取组内第一的数据行获取分组后,组内排名第一或最后的数据行。

前言&#xff1a; group by函数后取到的是分组中的第一条数据&#xff0c;但是我们有时候需要取出各分组的最新一条&#xff0c;该怎么实现呢&#xff1f; 本文提供两种实现方式。 一、准备数据 DROP TABLE IF EXISTS tb_dept; CREATE TABLE tb_dept (id bigint(20) UNSIG…

chat聊天系统消息消费时遇到的问题及优化思路

前言 之前有段工作经历涉及到了chat相关&#xff0c;而消息的发送 -> 存储 -> 消费是由不同的团队负责的&#xff0c;因此消息如何再多个团队之间流通、以及通过什么介质传递都是需要考虑的问题。 之前我负责过一些消息消费的相关工作&#xff0c;消息发送团队将消息推…

【Linux】简介磁盘|inode|动静态库

目录一.简介磁盘1.磁盘的物理结构&#xff1a;2.磁盘存储方式&#xff1a;3.磁盘的逻辑抽象&#xff1a;二.inode&&文件系统1.inode文件属性&#xff08;inode&#xff09;内容&#xff08;data block&#xff09;为什么删除一个文件相比于写一个文件要快得多&#xff…

若依配置教程(二)集成积木报表JimuReport

积木报表配置官网 在搭建好若依环境成功运行以后&#xff0c;我们先在这个系统中加一个小功能&#xff1a;JimuReport积木报表&#xff0c;以下步骤&#xff0c;我们按照官网教程&#xff0c;详细配置一下&#xff1a; 1.在ruoyi-admin文件夹下的pom.xml加入jar包依赖&#x…

MLP多层感知机理解

目录 .1简介 .2例子 2.1模型 2.2 实例 2.2.1 问题描述 2.2.2 数学过程 .3 代码 3.1 问题描述 3.2 代码 references&#xff1a; .1简介 多层感知机是全连接的 可以把低维的向量映射到高维度 MLP整个模型就是这样子的&#xff0c;上面说的这个三层的MLP用公式总结起来…

C 语言零基础入门教程(二十)

C 预处理器 C 预处理器不是编译器的组成部分&#xff0c;但是它是编译过程中一个单独的步骤。简言之&#xff0c;C 预处理器只不过是一个文本替换工具而已&#xff0c;它们会指示编译器在实际编译之前完成所需的预处理。我们将把 C 预处理器&#xff08;C Preprocessor&#x…

练手好福利!20个Python实战项目含源代码【2023最新】

高效学习源代码的步骤&#xff1a;1.运行程序&#xff0c;观察表现2.运行源码&#xff0c;断点调试&#xff0c;从头跟一边源码的执行流程&#xff0c;注意函数堆栈3.画类图、流程图&#xff0c;先把遇到的重要类记录下来&#xff0c;表明各个类的关系4.记录问题&#xff0c;把…

Unity XR

一、几个Unity XR Interaction Toolkit学习地址 1.B站视频 https://www.bilibili.com/video/BV11q4y1b74z/?spm_id_from333.999.0.0&vd_source8125d294022d2e63a58dfd228a7fcf63 https://www.bilibili.com/video/BV13b4y177J4/?spm_id_from333.999.0.0&vd_source8…

【对象的比较】java代码实现,详解对象的比较,Comparable接口和Comparator比较器

前言&#xff1a; 大家好&#xff0c;我是良辰丫&#xff0c;&#x1f49e;&#x1f49e;&#x1f49e;今天的我们要学习的知识点是java对象的比较&#xff0c;不是大家现实生活中对象的比较&#xff0c;是java中new一个对象的那个对象&#xff0c;对象的比较到底是什么意思呢&…

24.网络编程(二)

目录 三.TCP通信 3.1 TCP协议特点 3.2 TCP协议通信场景 3.3 TCP通信模型演示 3.4 Socket 3.5 ServerSocket 3.6 注意事项 3.7 案例 3.7.1 TCP通信—单发单收 3.7.2 TCP通信—多发多收 3.7.3 TCP通信—同时接收多个客户端的消息。 3.7.4 TCP通信—使用线程池优化&am…

工业相机和镜头

工业相机和镜头镜头型号数据电源接口定焦镜头的调焦景深景深大小光圈相机、镜头选取参考镜头型号、数据电源接口、定焦镜头的调焦、景深、景深大小、光圈、相机、镜头选取 镜头型号 C&#xff0c;CS系列&#xff1a;相机镜头的C、CS接口非常相似&#xff0c;它们的接口直径、螺…

检索业务:基本数据渲染和排错

采用标签显示商品的数据 <div class"rig_tab"><div th:each"product:${result.getProducts()}"><div class"ico"><i class"iconfont icon-weiguanzhu"></i><a href"/static/search/#">…

5、数据的重构

目录 一、为什么进行数据重构 二、如何进行数据重构 一、为什么进行数据重构 进行数据分析时&#xff0c;有可能会发现数据的结构并不适合直接进行数据分析操作&#xff0c;如下面数据&#xff0c;但通过复制-粘贴-转置等方法操作又太繁琐&#xff0c;数据量小还行&#xff…

C++ 图进阶系列之 kruskal 和 Prim 算法_图向最小生成树的华丽转身

1. 前言 树和图形状相似&#xff0c;也有差异性。树中添加一条或多条边&#xff0c;可成图。图中减小一条或多条边&#xff0c;可成树。形态的变化由数据之间的逻辑关系决定。 图用来描述数据之间多对多关系。树用来描述数据之间一对多关系。 思考如下问题&#xff1f; 如果…

esp32 烧录协议

esp32的rom固化了出场固件。进入烧录模式后&#xff0c;esp32串口输出&#xff1a;给esp32烧录固件的时候&#xff0c;需要和rom的bootloder进行通讯。通讯时&#xff0c;使用 SLIP 数据包帧进行双向数据传输。每个 SLIP 数据包都以 0xC0 开始和结束。 在数据包中&#xff0c;所…