Android ANR分析

什么是ANR

ANR(Application Not Responding)就是應(yīng)用在規(guī)定的時(shí)間內(nèi)沒有響應(yīng)用戶輸入或者其他應(yīng)用或者系統(tǒng)服務(wù)。

發(fā)生ANR的場(chǎng)景

  • Service超時(shí)

Service ANR一般是指AMS通過binder IPC調(diào)用Service CMD,如果在規(guī)定的時(shí)間內(nèi)沒有收到Service宿主進(jìn)程反饋的Service CMD執(zhí)行完成的消息焕盟,就會(huì)觸發(fā)Service ANR桃焕。這里Service CMD包括CREATE_SERVICE寸宏、BIND_SERVICE幕袱、UNBIND_SERVICE仪际、STOP_SERVICE等。

接下來我們以startService為例分析Service Timeout ANR的流程宪迟,如圖所示:


我們從startService開始分析Service超時(shí)ANR的流程酣衷。

    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, String callingPackage, int userId)
            throws TransactionTooLargeException {
            ...
            synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid, callingPackage, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

startService進(jìn)行一些簡(jiǎn)單的檢查工作后,直接調(diào)用ActiveServices對(duì)象的startServiceLocked踩验,進(jìn)一步調(diào)用bringUpServiceLocked來啟動(dòng)Service鸥诽。

    private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
        //Slog.i(TAG, "Bring up service:");
        //r.dump("  ");

        // 檢查服務(wù)是否已經(jīng)運(yùn)行
        if (r.app != null && r.app.thread != null) {
            sendServiceArgsLocked(r, execInFg, false);
            return null;
        }
        ...
        if (!isolated) {
            // 服務(wù)運(yùn)行在宿主應(yīng)用的進(jìn)程
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                    // 運(yùn)行服務(wù)
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortName, e);
                }

                // If a dead object exception was thrown -- fall through to
                // restart the application.
            }
        } else {
            // 服務(wù)運(yùn)行在單獨(dú)的進(jìn)程
            // If this service runs in an isolated process, then each time
            // we call startProcessLocked() we will get a new isolated
            // process, starting another process if we are currently waiting
            // for a previous process to come up.  To deal with this, we store
            // in the service any current isolated process it is running in or
            // waiting to have come up.
            app = r.isolatedProc;
        }
        ...
    }

bringUpServiceLocked首先進(jìn)行一些檢查工作兽泣,如果Service運(yùn)行在宿主應(yīng)用负蚊,調(diào)用realStartServiceLocked運(yùn)行服務(wù);如果Service運(yùn)行在單獨(dú)的進(jìn)程雨饺,首先要?jiǎng)?chuàng)建一個(gè)進(jìn)程袭异,然后運(yùn)行Service钠龙。這里我們只考慮前者。

    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
        if (app.thread == null) {
            throw new RemoteException();
        }
        ...
        // 根據(jù)服務(wù)類型設(shè)置超時(shí)Message
        bumpServiceExecutingLocked(r, execInFg, "create");
        // 更新LRU鏈表
        mAm.updateLruProcessLocked(app, false, null);
        // 更新相關(guān)進(jìn)程的OomAdj
        mAm.updateOomAdjLocked();
        ...
        try {
            if (LOG_SERVICE_START_STOP) {
                String nameTerm;
                int lastPeriod = r.shortName.lastIndexOf('.');
                nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
                EventLogTags.writeAmCreateService(
                        r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
            }
            synchronized (r.stats.getBatteryStats()) {
                r.stats.startLaunchedLocked();
            }
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            // 通過binder IPC運(yùn)行服務(wù)
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
            r.postNotification();
            created = true;
        }
        ...
    }

realStartServiceLocked的參數(shù)execInFg表示服務(wù)是前臺(tái)服務(wù)還是后臺(tái)服務(wù)御铃,bumpServiceExecutingLocked會(huì)根據(jù)服務(wù)的類型設(shè)定不同的超時(shí)時(shí)間碴里,其中前臺(tái)服務(wù)的超時(shí)時(shí)間為SERVICE_TIMEOUT(20s),后臺(tái)服務(wù)的超時(shí)時(shí)間為10 * SERVICE_TIMEOUT上真。如果超時(shí)時(shí)間結(jié)束時(shí)咬腋,超時(shí)消息仍然沒有被移除,就表明有Service執(zhí)行超時(shí)睡互,下面分析ServiceTimeout根竿。

    void serviceTimeout(ProcessRecord proc) {
        String anrMessage = null;

        synchronized(mAm) {
            if (proc.executingServices.size() == 0 || proc.thread == null) {
                return;
            }
            final long now = SystemClock.uptimeMillis();
            final long maxTime =  now -
                    (proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
            ServiceRecord timeout = null;
            long nextTime = 0;
            for (int i=proc.executingServices.size()-1; i>=0; i--) {
                // 遍歷正在執(zhí)行的服務(wù),找到執(zhí)行超時(shí)的服務(wù)
                ServiceRecord sr = proc.executingServices.valueAt(i);
                if (sr.executingStart < maxTime) {
                    timeout = sr;
                    break;
                }
                if (sr.executingStart > nextTime) {
                    nextTime = sr.executingStart;
                }
            }
            if (timeout != null && mAm.mLruProcesses.contains(proc)) {
                // 找到執(zhí)行超時(shí)的服務(wù)
                Slog.w(TAG, "Timeout executing service: " + timeout);
                StringWriter sw = new StringWriter();
                PrintWriter pw = new FastPrintWriter(sw, false, 1024);
                pw.println(timeout);
                timeout.dump(pw, "    ");
                pw.close();
                mLastAnrDump = sw.toString();
                mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
                mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
                anrMessage = "executing service " + timeout.shortName;
            } else {
                // 沒有單個(gè)服務(wù)執(zhí)行超時(shí)就珠,繼續(xù)設(shè)定超時(shí)Message
                Message msg = mAm.mHandler.obtainMessage(
                        ActivityManagerService.SERVICE_TIMEOUT_MSG);
                msg.obj = proc;
                mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
                        ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
            }
        }

        if (anrMessage != null) {
            // 處理Service ANR
            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
        }
    }

serviceTimeout負(fù)責(zé)找到執(zhí)行超時(shí)的服務(wù)寇壳,然后調(diào)用AppErrors的appNotResponding處理。


上面的整個(gè)過程就是appNotResponding的處理流程妻怎,對(duì)于細(xì)節(jié)我們?cè)诜治鯝NR一節(jié)中再討論壳炎,需要注意的
是紅色區(qū)域的流程在Android N上已經(jīng)改變(改善系統(tǒng)性能),詳情參考下面鏈接逼侦。

https://android.googlesource.com/platform/frameworks/base/+/13eb2e20a30abcfa45f41cd11a1fc110717c7c2b%5E%21/

  • Broadcast超時(shí)

在分析Broadcast ANR之前我們先簡(jiǎn)單了解下Broadcast匿辩。
Broadcast一般分為兩類:

  • Normal broadcasts (sent with Context.sendBroadcast) are completely asynchronous. All receivers of the broadcast are run in an undefined order,
    often at the same time. This is more efficient, but means that receivers cannot use the result or abort APIs included here.
  • Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.

Even in the case of normal broadcasts, the system may in some situations revert to delivering the broadcast one receiver at a time. In particular, for receivers that may require the creation of a process, only one will be run at a time to avoid overloading the system with new processes. In this situation, however, the non-ordered semantics hold: these receivers still cannot return results or abort their broadcast.

BroadcastReceiver有兩種注冊(cè)方式:

You can either dynamically register an instance of this class with Context.registerReceiver() or statically publish an implementation through the receiver tag in your AndroidManifest.xml.

下面分析Broadcast ANR的流程,如圖所示:


如果AMS將Broadcast發(fā)送給廣播接收機(jī)后榛丢,在規(guī)定的時(shí)間內(nèi)沒有收到廣播接收機(jī)
發(fā)送的finishReceiver的消息铲球,就會(huì)觸發(fā)BroadcastTimeout ANR。下面從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) {
        ...
        // Figure out who all will receive this broadcast.
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            // 收集靜態(tài)注冊(cè)的廣播接收機(jī)
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == Process.SHELL_UID) {
                // Query one target user at a time, excluding shell-restricted users
                for (int i = 0; i < users.length; i++) {
                    if (mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                // 查找動(dòng)態(tài)注冊(cè)的廣播接收機(jī)
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId);
            }
        }
        ...
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            // 發(fā)送普通廣播到動(dòng)態(tài)注冊(cè)的廣播接收機(jī)
            // If we are not serializing this broadcast, then send the
            // registered receivers separately so they don't wait for the
            // components to be launched.
            // 根據(jù)廣播類型決定發(fā)送廣播的隊(duì)列,前臺(tái)廣播由前臺(tái)廣播對(duì)表處理挠阁;
            // 后臺(tái)廣播由后臺(tái)廣播隊(duì)列處理
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            // 創(chuàng)建廣播記錄
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                    appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                    resultExtras, ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
            final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
            if (!replaced) {
                // 將(前臺(tái)\后臺(tái))普通廣播放入(前臺(tái)\后臺(tái))并行廣播列表中
                queue.enqueueParallelBroadcastLocked(r);
                // 處理(前臺(tái)\后臺(tái))并行廣播列表中的廣播
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

        // Merge into one list.
        // 動(dòng)態(tài)注冊(cè)的廣播接收機(jī)宾肺、靜態(tài)注冊(cè)的廣播接收機(jī)按優(yōu)先級(jí)排序(高->低)溯饵,
        // 存放到receivers中
        int ir = 0;
        if (receivers != null) {
            ...
            int NT = receivers != null ? receivers.size() : 0;
            int it = 0;
            ResolveInfo curt = null;
            BroadcastFilter curr = null;
            while (it < NT && ir < NR) {
                if (curt == null) {
                    curt = (ResolveInfo)receivers.get(it);
                }
                if (curr == null) {
                    curr = registeredReceivers.get(ir);
                }
                if (curr.getPriority() >= curt.priority) {
                    // Insert this broadcast record into the final list.
                    receivers.add(it, curr);
                    ir++;
                    curr = null;
                    it++;
                    NT++;
                } else {
                    // Skip to the next ResolveInfo in the final list.
                    it++;
                    curt = null;
                }
            }
        }
        while (ir < NR) {
            if (receivers == null) {
                receivers = new ArrayList();
            }
            receivers.add(registeredReceivers.get(ir));
            ir++;
        }

        if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            // 根據(jù)廣播類型決定發(fā)送廣播的隊(duì)列,前臺(tái)廣播由前臺(tái)廣播對(duì)表處理锨用;
            // 后臺(tái)廣播由后臺(tái)廣播隊(duì)列處理
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, resolvedType,
                    requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                    resultData, resultExtras, ordered, sticky, false, userId);

            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                    + ": prev had " + queue.mOrderedBroadcasts.size());
            if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                    "Enqueueing broadcast " + r.intent.getAction());

            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
            if (!replaced) {
                // 將(前臺(tái)\后臺(tái))普通廣播放入(前臺(tái)\后臺(tái))有序廣播列表中
                queue.enqueueOrderedBroadcastLocked(r);
                // 處理(前臺(tái)\后臺(tái))有序廣播列表中的廣播
                queue.scheduleBroadcastsLocked();
            }
        }
        ...
    }

broadcastIntentLocked在進(jìn)行一系列的檢查以及特殊情況的處理后丰刊,按廣播的類型以及相應(yīng)的廣播接收機(jī)的類型進(jìn)行分發(fā)。

下面分析分發(fā)函數(shù)scheduleBroadcastsLocked

    public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        // 發(fā)送處理廣播的Message
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }

scheduleBroadcastsLocked只是簡(jiǎn)單的發(fā)送BROADCAST_INTENT_MSG消息增拥,該消息的處理函數(shù)調(diào)用processNextBroadcast進(jìn)行分發(fā)啄巧。

    final void processNextBroadcast(boolean fromMsg) {
        synchronized(mService) {
            BroadcastRecord r;

            ...
            mService.updateCpuStats();
            ...
            // First, deliver any non-serialized broadcasts right away.
            while (mParallelBroadcasts.size() > 0) {
                r = mParallelBroadcasts.remove(0);
                r.dispatchTime = SystemClock.uptimeMillis();
                r.dispatchClockTime = System.currentTimeMillis();
                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);
                    //將廣播同時(shí)發(fā)送給Parallel列表中的廣播接收機(jī)
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }
            ...
            do {
                if (mOrderedBroadcasts.size() == 0) {
                    // No more broadcasts pending, so all done!
                    mService.scheduleAppGcsLocked();
                    if (looped) {
                        // If we had finished the last ordered broadcast, then
                        // make sure all processes have correct oom and sched
                        // adjustments.
                        mService.updateOomAdjLocked();
                    }
                    return;
                }
                // 將廣播按照優(yōu)先級(jí)一個(gè)一個(gè)的分發(fā)給Ordered列表中的廣播接收機(jī)
                r = mOrderedBroadcasts.get(0);
                boolean forceReceive = false;
                ...
                int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
                if (mService.mProcessesReady && r.dispatchTime > 0) {
                    // 1) 廣播發(fā)送始于SystemReady之前,結(jié)束于SystemReady之后的超時(shí)檢測(cè)
                    // 由于SystemReady之前的廣播發(fā)送可能很慢掌栅,而且不檢測(cè)秩仆,所以超時(shí)時(shí)間為
                    // 2 * mTimeoutPeriod * numReceivers
                    // 2) 廣播發(fā)送過程中有dex2oat發(fā)生。
                    long now = SystemClock.uptimeMillis();
                    if ((numReceivers > 0) &&
                            (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                        ...
                        broadcastTimeoutLocked(false); // forcibly finish this broadcast
                        forceReceive = true;
                        r.state = BroadcastRecord.IDLE;
                    }
                }
                ...
                if (r.receivers == null || r.nextReceiver >= numReceivers
                        || r.resultAbort || forceReceive) {
                    // No more receivers for this broadcast!  Send the final
                    // result if requested...
                    if (r.resultTo != null) {
                        // 廣播發(fā)送完成猾封,如果發(fā)送方需要結(jié)果澄耍,將結(jié)果反饋給發(fā)送方。
                        try {
                            if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                                    "Finishing broadcast [" + mQueueName + "] "
                                    + r.intent.getAction() + " app=" + r.callerApp);
                            performReceiveLocked(r.callerApp, r.resultTo,
                                new Intent(r.intent), r.resultCode,
                                r.resultData, r.resultExtras, false, false, r.userId);
                            // Set this to null so that the reference
                            // (local and remote) isn't kept in the mBroadcastHistory.
                            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");
                    // 一個(gè)廣播的所有接收機(jī)發(fā)送完成晌缘,取消超時(shí)消息設(shè)置齐莲。
                    cancelBroadcastTimeoutLocked();

                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
                            "Finished with ordered broadcast " + r);

                    // ... and on to the next...
                    addBroadcastToHistoryLocked(r);
                    if (r.intent.getComponent() == null && r.intent.getPackage() == null
                            && (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
                        // This was an implicit broadcast... let's record it for posterity.
                        mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
                                r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
                    }
                    // 從Ordered隊(duì)列中移除發(fā)送完成的廣播
                    mOrderedBroadcasts.remove(0);
                    r = null;
                    looped = true;
                    continue;
                }
            } while (r == null);

            // Get the next receiver...
            // 獲取廣播的下一個(gè)接收者(可能有多個(gè))發(fā)送
            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();
            if (recIdx == 0) {
                // 廣播多個(gè)接收者中的第一個(gè),記錄分發(fā)時(shí)間
                r.dispatchTime = r.receiverTime;
                r.dispatchClockTime = System.currentTimeMillis();
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
                        + mQueueName + "] " + r);
            }
            if (! mPendingBroadcastTimeoutMessage) {
                long timeoutTime = r.receiverTime + mTimeoutPeriod;
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Submitting BROADCAST_TIMEOUT_MSG ["
                        + mQueueName + "] for " + r + " at " + timeoutTime);
                // 如果沒有設(shè)定廣播發(fā)送超時(shí)時(shí)間磷箕,在這里設(shè)定
                setBroadcastTimeoutLocked(timeoutTime);
            }
            ...
            final Object nextReceiver = r.receivers.get(recIdx);

            if (nextReceiver instanceof BroadcastFilter) {
                // Simple case: this is a registered receiver who gets
                // a direct call.
                BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                        "Delivering ordered ["
                        + mQueueName + "] to registered "
                        + filter + ": " + r);
                // 如果是動(dòng)態(tài)注冊(cè)的廣播接收機(jī)选酗,直接發(fā)送
                deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
                // 我的理解r.ordered == true ???
                if (r.receiver == null || !r.ordered) {
                    // The receiver has already finished, so schedule to
                    // process the next one.
                    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
                            + mQueueName + "]: ordered="
                            + r.ordered + " receiver=" + r.receiver);
                    r.state = BroadcastRecord.IDLE;
                    scheduleBroadcastsLocked();
                } else {
                    if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
                        scheduleTempWhitelistLocked(filter.owningUid,
                                brOptions.getTemporaryAppWhitelistDuration(), r);
                    }
                }
                return;
            }
            ...
            // Is this receiver's application already running?
            if (app != null && app.thread != null) {
                // 廣播接收機(jī)Host進(jìn)程已經(jīng)運(yùn)行,發(fā)送廣播
                try {
                    app.addPackage(info.activityInfo.packageName,
                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                    // 最終通過Binder IPC運(yùn)行廣播接收機(jī)        
                    processCurBroadcastLocked(r, app);
                    return;
                }
            }
            ...
            // 創(chuàng)建廣播接收機(jī)Host進(jìn)程
            if ((r.curApp=mService.startProcessLocked(targetProcess,
                    info.activityInfo.applicationInfo, true,
                    r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                    "broadcast", r.curComponent,
                    (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                            == null) {
                // Ah, this recipient is unavailable.  Finish it if necessary,
                // and mark the broadcast record as ready for the next.
                Slog.w(TAG, "Unable to launch app "
                        + info.activityInfo.applicationInfo.packageName + "/"
                        + info.activityInfo.applicationInfo.uid + " for broadcast "
                        + r.intent + ": process is bad");
                logBroadcastReceiverDiscardLocked(r);
                finishReceiverLocked(r, r.resultCode, r.resultData,
                        r.resultExtras, r.resultAbort, false);
                scheduleBroadcastsLocked();
                r.state = BroadcastRecord.IDLE;
                return;
            }

            mPendingBroadcast = r;
        }
    }

簡(jiǎn)單總結(jié)一下廣播的發(fā)送超時(shí)流程:


與服務(wù)超時(shí)ANR類似岳枷,如果在規(guī)定的時(shí)間內(nèi)芒填,廣播超時(shí)消息沒有取消,就會(huì)觸發(fā)ANR嫩舟。接下來分析
廣播超時(shí)ANR的處理氢烘。

    final void broadcastTimeoutLocked(boolean fromMsg) {
        // fromMsg標(biāo)記超時(shí)觸發(fā)者,true表示超時(shí)消息觸發(fā)
        // false表示直接調(diào)用超時(shí)處理
        if (fromMsg) {
            mPendingBroadcastTimeoutMessage = false;
        }

        if (mOrderedBroadcasts.size() == 0) {
            return;
        }

        long now = SystemClock.uptimeMillis();
        BroadcastRecord r = mOrderedBroadcasts.get(0);
        if (fromMsg) {
            if (mService.mDidDexOpt) {
                // Delay timeouts until dexopt finishes.
                mService.mDidDexOpt = false;
                long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
            if (!mService.mProcessesReady) {
                // Only process broadcast timeouts if the system is ready. That way
                // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended
                // to do heavy lifting for system up.
                return;
            }

            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            // 如果發(fā)送給當(dāng)前廣播接收機(jī)(可能多個(gè))沒有超時(shí)家厌,則重新設(shè)定超時(shí)消息播玖;從這里
            // 看出超時(shí)其實(shí)是針對(duì)單個(gè)廣播接收機(jī),如果多個(gè)廣播接收機(jī)收發(fā)累計(jì)時(shí)間
            // 超時(shí)饭于,并不會(huì)觸發(fā)ANR蜀踏。
            if (timeoutTime > now) {
                // We can observe premature timeouts because we do not cancel and reset the
                // broadcast timeout message after each receiver finishes.  Instead, we set up
                // an initial timeout then kick it down the road a little further as needed
                // when it expires.
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                        "Premature timeout ["
                        + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
                        + timeoutTime);
                setBroadcastTimeoutLocked(timeoutTime);
                return;
            }
        }
        ...
        // 觸發(fā)廣播超時(shí)ANR
        if (anrMessage != null) {
            // Post the ANR to the handler since we do not want to process ANRs while
            // potentially holding our lock.
            mHandler.post(new AppNotResponding(app, anrMessage));
        }
    }

broadcastTimeoutLocked根據(jù)參數(shù)fromMsg進(jìn)一步判定是否確實(shí)廣播超時(shí)ANR,這里要注意如果有dex2oat掰吕,廣播超時(shí)時(shí)間被推遲果覆;如果系統(tǒng)啟動(dòng)還未就緒,不檢測(cè)廣播超時(shí)殖熟。廣播超時(shí)ANR的處理流程跟服務(wù)超時(shí)ANR類似局待,不再贅述。

  • ContentProvider超時(shí)

Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface.
A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider.
If you don't need to share data amongst multiple applications you can use a database directly via SQLiteDatabase.

ContentProvider超時(shí)的示意圖如上圖所示,如果我們只是使用getContentResolver返回的對(duì)象(ApplicationContentResolver)來訪問ContentProvider提供的數(shù)據(jù)钳榨,并不會(huì)有超時(shí)ANR的檢測(cè)舰罚。當(dāng)使用ContentResolverClient來訪問數(shù)據(jù)才會(huì)有超時(shí)ANR檢測(cè)。ContentResolverClient檢測(cè)到數(shù)據(jù)操作超時(shí)后薛耻,最終通過Binder IPC通知AMS营罢,AMS的處理與之前討論的類似,不再贅述饼齿。

  • Input Dispatching超時(shí)

我們以Touch事件來說明Input Dispatching超時(shí)機(jī)制饲漾。這里只分析關(guān)鍵的函數(shù),首先看
checkWindowReadyForMoreInputLocked缕溉。

String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
        const char* targetType) {
    ...
    // Ensure that the dispatch queues aren't too far backed up for this event.
    if (eventEntry->type == EventEntry::TYPE_KEY) {
        // 對(duì)于按鍵事件處理考传,如果outboundQueue以及waitQueue均不為空的話
        // 不能發(fā)送。
        // If the event is a key event, then we must wait for all previous events to
        // complete before delivering it because previous events may have the
        // side-effect of transferring focus to a different window and we want to
        // ensure that the following keys are sent to the new window.
        //
        // Suppose the user touches a button in a window then immediately presses "A".
        // If the button causes a pop-up window to appear then we want to ensure that
        // the "A" key is delivered to the new pop-up window.  This is because users
        // often anticipate pending UI changes when typing on a keyboard.
        // To obtain this behavior, we must serialize key events with respect to all
        // prior input events.
        if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) {
            return String8::format("Waiting to send key event because the %s window has not "
                    "finished processing all of the input events that were previously "
                    "delivered to it.  Outbound queue length: %d.  Wait queue length: %d.",
                    targetType, connection->outboundQueue.count(), connection->waitQueue.count());
        }
    } else {
        // Touch事件的處理倒淫,如果waitQueue不為空伙菊,且waitQueue的head事件分發(fā)完成距離當(dāng)前已經(jīng)
        // 超過STREAM_AHEAD_EVENT_TIMEOUT,不能發(fā)送敌土,
        // Touch events can always be sent to a window immediately because the user intended
        // to touch whatever was visible at the time.  Even if focus changes or a new
        // window appears moments later, the touch event was meant to be delivered to
        // whatever window happened to be on screen at the time.
        //
        // Generic motion events, such as trackball or joystick events are a little trickier.
        // Like key events, generic motion events are delivered to the focused window.
        // Unlike key events, generic motion events don't tend to transfer focus to other
        // windows and it is not important for them to be serialized.  So we prefer to deliver
        // generic motion events as soon as possible to improve efficiency and reduce lag
        // through batching.
        //
        // The one case where we pause input event delivery is when the wait queue is piling
        // up with lots of events because the application is not responding.
        // This condition ensures that ANRs are detected reliably.
        if (!connection->waitQueue.isEmpty()
                && currentTime >= connection->waitQueue.head->deliveryTime
                        + STREAM_AHEAD_EVENT_TIMEOUT) {
            return String8::format("Waiting to send non-key event because the %s window has not "
                    "finished processing certain input events that were delivered to it over "
                    "%0.1fms ago.  Wait queue length: %d.  Wait queue head age: %0.1fms.",
                    targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f,
                    connection->waitQueue.count(),
                    (currentTime - connection->waitQueue.head->deliveryTime) * 0.000001f);
        }
    }
    ...
}

checkWindowReadyForMoreInputLocked用于檢查目標(biāo)窗口是否已經(jīng)準(zhǔn)備好接收更多的輸入事件镜硕,如果沒有準(zhǔn)備好,需要進(jìn)一步檢查窗口沒有準(zhǔn)備好的原因返干,下面分析handleTargetsNotReadyLocked兴枯。

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime,
        const EventEntry* entry,
        const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t* nextWakeupTime, const char* reason) {
    ...
    if (applicationHandle == NULL && windowHandle == NULL) {
        ...
    } else {
        // 記錄首次目標(biāo)窗口沒有準(zhǔn)備好的時(shí)間,并計(jì)算出超時(shí)時(shí)間mInputTargetWaitTimeoutTime
        if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) {
#if DEBUG_FOCUS
            ALOGD("Waiting for application to become ready for input: %s.  Reason: %s",
                    getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(),
                    reason);
#endif
            nsecs_t timeout;
            if (windowHandle != NULL) {
                timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else if (applicationHandle != NULL) {
                timeout = applicationHandle->getDispatchingTimeout(
                        DEFAULT_INPUT_DISPATCHING_TIMEOUT);
            } else {
                timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
            }

            mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
            mInputTargetWaitStartTime = currentTime;
            mInputTargetWaitTimeoutTime = currentTime + timeout;
            mInputTargetWaitTimeoutExpired = false;
            mInputTargetWaitApplicationHandle.clear();

            ...
        }
    }
    ...
   if (currentTime >= mInputTargetWaitTimeoutTime) {
        // Input Dispatching ANR處理    
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);

        // Force poll loop to wake up immediately on next iteration once we get the
        // ANR response back from the policy.
        *nextWakeupTime = LONG_LONG_MIN;
        return INPUT_EVENT_INJECTION_PENDING;
   }
   ...
}

AMS對(duì)于Input Dispatching超時(shí)ANR的處理與之前類似矩欠,不再贅述财剖。

如何分析ANR

  • 問題描述

ANR in com.samsung.android.email.provider during CAS Test

  • Log分析

    • ANR發(fā)生的準(zhǔn)確時(shí)間及原因
    01-03 03:47:07.488  1149  1313 I am_anr  : [0,4910,com.samsung.android.email.provider,953695813,Input dispatching timed out (Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 4.  Wait queue head age: 5615.7ms.)]
    
    • Email進(jìn)程的backtrace(/data/anr/traces.txt or dropbox)

    通常重點(diǎn)關(guān)注mian線程以及binder線程

    "main" prio=5 tid=1 Native
    | group="main" sCount=1 dsCount=0 obj=0x768f3fb8 self=0x557be4ec40
    | sysTid=4910 nice=0 cgrp=default sched=0/0 handle=0x7f8bb4ffd0
    | state=S schedstat=( 15510426384 7139276978 32108 ) utm=1214 stm=337 core=4 HZ=100
    | stack=0x7fdda0b000-0x7fdda0d000 stackSize=8MB
    | held mutexes=
    kernel: __switch_to+0x7c/0x88
    kernel: SyS_epoll_wait+0x2cc/0x394
    kernel: SyS_epoll_pwait+0x9c/0x114
    kernel: __sys_trace+0x48/0x4c
    native: #00 pc 0000000000069d94  /system/lib64/libc.so (__epoll_pwait+8)
    native: #01 pc 000000000001ce64  /system/lib64/libc.so (epoll_pwait+32)
    native: #02 pc 000000000001be88  /system/lib64/libutils.so (_ZN7android6Looper9pollInnerEi+144)
    native: #03 pc 000000000001c268  /system/lib64/libutils.so (_ZN7android6Looper8pollOnceEiPiS1_PPv+80)
    native: #04 pc 00000000000d89dc  /system/lib64/libandroid_runtime.so (_ZN7android18NativeMessageQueue8pollOnceEP7_JNIEnvP8_jobjecti+48)
    native: #05 pc 000000000000087c  /system/framework/arm64/boot.oat (Java_android_os_MessageQueue_nativePollOnce__JI+144)
    at android.os.MessageQueue.nativePollOnce(Native method)
    at android.os.MessageQueue.next(MessageQueue.java:323)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:7421)
    at java.lang.reflect.Method.invoke!(Native method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
    
    "Binder_1" prio=5 tid=8 Native
    | group="main" sCount=1 dsCount=0 obj=0x32c072e0 self=0x557c129ec0
    | sysTid=4921 nice=0 cgrp=default sched=0/0 handle=0x7f86c55450
    | state=S schedstat=( 518922106 635149861 2589 ) utm=25 stm=26 core=4 HZ=100
    | stack=0x7f86b59000-0x7f86b5b000 stackSize=1013KB
    | held mutexes=
    kernel: __switch_to+0x7c/0x88
    kernel: binder_thread_read+0xf60/0x10d8
    kernel: binder_ioctl_write_read+0x1cc/0x304
    kernel: binder_ioctl+0x348/0x738
    kernel: do_vfs_ioctl+0x4e0/0x5c0
    kernel: SyS_ioctl+0x5c/0x88
    kernel: __sys_trace+0x48/0x4c
    native: #00 pc 0000000000069e80  /system/lib64/libc.so (__ioctl+4)
    native: #01 pc 0000000000073ea4  /system/lib64/libc.so (ioctl+100)
    native: #02 pc 000000000002d584  /system/lib64/libbinder.so (_ZN7android14IPCThreadState14talkWithDriverEb+164)
    native: #03 pc 000000000002de00  /system/lib64/libbinder.so (_ZN7android14IPCThreadState20getAndExecuteCommandEv+24)
    native: #04 pc 000000000002df1c  /system/lib64/libbinder.so (_ZN7android14IPCThreadState14joinThreadPoolEb+76)
    native: #05 pc 0000000000036a10  /system/lib64/libbinder.so (???)
    native: #06 pc 00000000000167b4  /system/lib64/libutils.so (_ZN7android6Thread11_threadLoopEPv+208)
    native: #07 pc 00000000000948d0  /system/lib64/libandroid_runtime.so (_ZN7android14AndroidRuntime15javaThreadShellEPv+96)
    native: #08 pc 0000000000016004  /system/lib64/libutils.so (???)
    native: #09 pc 0000000000067904  /system/lib64/libc.so (_ZL15__pthread_startPv+52)
    native: #10 pc 000000000001c804  /system/lib64/libc.so (__start_thread+16)
    
    ...
    
    • ANR發(fā)生時(shí)系統(tǒng)CPU使用信息
    01-03 03:47:28.928  1149  1313 E android.os.Debug: ro.product_ship = false
    01-03 03:47:28.928  1149  1313 E android.os.Debug: ro.debug_level = 0x494d
    01-03 03:47:28.928  1149  1313 E android.os.Debug: Failed open /proc/schedinfo
    01-03 03:47:28.928  1149  1313 E ActivityManager: ANR in com.samsung.android.email.provider (com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL)
    01-03 03:47:28.928  1149  1313 E ActivityManager: PID: 4910
    01-03 03:47:28.928  1149  1313 E ActivityManager: Reason: Input dispatching timed out (Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 4.  Wait queue head age: 5615.7ms.)
    01-03 03:47:28.928  1149  1313 E ActivityManager: Load: 0.0 / 0.0 / 0.0
    01-03 03:47:28.928  1149  1313 E ActivityManager: ------ Current CPU Core Info ------
    01-03 03:47:28.928  1149  1313 E ActivityManager: - offline : 
    01-03 03:47:28.928  1149  1313 E ActivityManager: - online : 0-7
    01-03 03:47:28.928  1149  1313 E ActivityManager: - cpu_normalized_load : -
    01-03 03:47:28.928  1149  1313 E ActivityManager: - run_queue_avg : 2.9
    01-03 03:47:28.928  1149  1313 E ActivityManager: - AP Temp = 475
    01-03 03:47:28.928  1149  1313 E ActivityManager:                              0           1           2           3           4           5           6           7
    01-03 03:47:28.928  1149  1313 E ActivityManager: ------------------------------------------------------------------------------------------------------------------
    01-03 03:47:28.928  1149  1313 E ActivityManager: scaling_cur_freq       1689600     1689600     1689600     1689600     1689600     1689600     1689600     1689600
    01-03 03:47:28.928  1149  1313 E ActivityManager: scaling_governor   interactive interactive interactive interactive interactive interactive interactive interactive
    01-03 03:47:28.928  1149  1313 E ActivityManager: scaling_max_freq       1689600     1689600     1689600     1689600     1689600     1689600     1689600     1689600
    01-03 03:47:28.928  1149  1313 E ActivityManager: ------------------------------------------------------------------------------------------------------------------
    01-03 03:47:28.928  1149  1313 E ActivityManager: CPU usage from 1277ms to -51ms ago:
    01-03 03:47:28.928  1149  1313 E ActivityManager:   100% 7807/procrank: 10% user + 89% kernel / faults: 6598 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   36% 466/surfaceflinger: 9% user + 27% kernel / faults: 596 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   25% 1149/system_server: 4.4% user + 20% kernel / faults: 139 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   21% 259/spi3: 0% user + 21% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   10% 4910/com.samsung.android.email.provider: 7.2% user + 3.6% kernel / faults: 39 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   8.6% 2837/com.samsung.android.providers.context: 3.6% user + 5% kernel / faults: 28 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   3.4% 7806/dumpstate: 0.2% user + 3.1% kernel / faults: 26 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   8.3% 506/mediaserver: 2.2% user + 6% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   6.8% 320/mmcqd/0: 0% user + 6.8% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   1.5% 6263/com.sec.spp.push:RemoteDlcProcess: 1.2% user + 0.3% kernel / faults: 547 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   5% 5677/com.samsung.cas: 1.4% user + 3.6% kernel / faults: 463 minor
    01-03 03:47:28.928  1149  1313 E ActivityManager:   5% 7751/screenrecord: 2.1% user + 2.8% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   4.5% 29/ksoftirqd/4: 0% user + 4.5% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   4.4% 913/kworker/u16:9: 0% user + 4.4% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   3.7% 34/ksoftirqd/5: 0% user + 3.7% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   3.7% 73/kworker/u16:3: 0% user + 3.7% kernel
    01-03 03:47:28.928  1149  1313 E ActivityManager:   3.7% 262/irq/69-madera: 0% user + 3.7% kernel
    ...
    01-03 03:47:28.928  1149  1313 E ActivityManager: 62% TOTAL: 9.5% user + 50% kernel + 1.3% iowait + 0.5% softirq
    01-03 03:47:28.928  1149  1313 E ActivityManager: CPU usage from 189522ms to 189522ms ago with 0% awake:
    01-03 03:47:28.928  1149  1313 E ActivityManager: 0% TOTAL: 0% user + 0% kernel
    
    • 超時(shí)ANR期間內(nèi)log(以及Service/Broadcast history等)
      ...
      Line 33430: 01-03 03:47:01.858  1149  1550 I InputDispatcher: Delivering touch to (7647): x: 520.000, y: 1562.000, flags=0x0, action: 0x0, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0
      ...
      Line 33432: 01-03 03:47:01.858  1149  1550 I InputDispatcher: Delivering touch to (7647): x: 520.456, y: 1560.577, flags=0x0, action: 0x1, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0
      ...
      Line 33447: 01-03 03:47:02.158  1149  1550 I InputDispatcher: Delivering touch to (7647): x: 369.000, y: 1089.000, flags=0x0, action: 0x0, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0
      ...
      Line 33449: 01-03 03:47:02.158  1149  1550 I InputDispatcher: Delivering touch to (7647): x: 375.404, y: 1090.664, flags=0x0, action: 0x1, channel '8d82752 ScrollCaptureUiService (server)', toolType: 0
      Line 33481: 01-03 03:47:02.468  1149  1550 D InputDispatcher: Waiting for application to become ready for input: AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}} - Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}.  Reason: Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 4.  Wait queue head age: 610.8ms.
      Line 33825: 01-03 03:47:07.468  1149  1550 I InputDispatcher: Application is not responding: AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}} - Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}.  It has been 5007.8ms since event, 5004.9ms since wait started.  Reason: Waiting to send non-key event because the focused window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 4.  Wait queue head age: 5615.7ms.
    

    首先分析Email在發(fā)生ANR之前5~6內(nèi)的事件分發(fā)log,我們從log中發(fā)現(xiàn)在這段事件內(nèi),InputDispatcher并沒有分發(fā)輸入事件給Email的窗口癌淮,而是一直有輸入時(shí)間分發(fā)給ScrollCaptureUiService躺坟。繼續(xù)看發(fā)現(xiàn)實(shí)際上是ScrollCaptureUiService窗口不響應(yīng)輸入事件(根據(jù)waitQueue中事件數(shù)量以及事件超時(shí)時(shí)間可判定),為什么ScrollCaptureUiService不響應(yīng)輸入事件乳蓄?繼續(xù)看log發(fā)現(xiàn)在收到輸入事件的時(shí)候咪橙,com.samsung.android.app.scrollcapture發(fā)生Crash,這就可以解釋ScrollCaptureUiService窗口不響應(yīng)輸入事件啦(PS:細(xì)節(jié)后續(xù)討論debuggerd64時(shí)候再展開)虚倒。

    ...
    01-03 03:47:01.618  7647  7647 F libc    : heap corruption detected by dlmalloc
    01-03 03:47:01.618  7647  7647 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 7647 (p.scrollcapture)
    01-03 03:47:01.628  7647  7777 D SC_ScrollCapture_JNI: getRgbSumTable : W=1080  H=1920  SumW=1027 SumH=1920 Elapsed=10ms
    01-03 03:47:01.698   501   501 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    01-03 03:47:01.698   501   501 F DEBUG   : Build fingerprint: 'Android/c5proltezc/c5proltechn:6.0.1/MMB29M/C5010ZCE0APJ1c:eng/test-keys'
    01-03 03:47:01.698   501   501 F DEBUG   : Revision: '0'
    01-03 03:47:01.698   501   501 F DEBUG   : ABI: 'arm64'
    01-03 03:47:01.698   501   501 F DEBUG   : pid: 7647, tid: 7647, name: p.scrollcapture  >>> com.samsung.android.app.scrollcapture
    ...
    

    既然是ScrollCaptureUiService窗口不響應(yīng)輸入事件為什么Email發(fā)生ANR?

    InputDispatcher: Waiting for application to become ready for input: AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}} - Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}
    

    從log中我們看出InputApplicationHandle是AppWindowToken{d0aeba2b3 token=Token{7242822 ActivityRecord{b24b1ed u0 com.samsung.android.email.provider/com.samsung.android.email.ui.activity.MessageListXL t1044}}美侦,但是InputWindowHandle是Window{8d82752 u0 d0 p7647 ScrollCaptureUiService},我們可以理解為窗口覆蓋在應(yīng)用之上魂奥。對(duì)于窗口與應(yīng)用handle不一致的情況菠剩,發(fā)生ANR時(shí),首先找到窗口handle也就是Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}對(duì)應(yīng)的應(yīng)用耻煤,如果為空的話具壮,系統(tǒng)認(rèn)為應(yīng)用handle對(duì)應(yīng)的應(yīng)用也就是com.samsung.android.email.providerANR准颓,這就是Email發(fā)生ANR的原因。

如何避免ANR

這里大家可以參考Android Developer網(wǎng)站給出建議棺妓,理解了ANR發(fā)生的原理瞬场,自然也就懂得避免ANR。

參考

  1. https://developer.android.com/training/articles/perf-anr.html
  2. http://gityuan.com/2016/07/02/android-anr/
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涧郊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子眼五,更是在濱河造成了極大的恐慌妆艘,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件看幼,死亡現(xiàn)場(chǎng)離奇詭異批旺,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诵姜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門汽煮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棚唆,你說我怎么就攤上這事暇赤。” “怎么了宵凌?”我有些...
    開封第一講書人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵鞋囊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瞎惫,道長(zhǎng)溜腐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任瓜喇,我火速辦了婚禮挺益,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乘寒。我一直安慰自己望众,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開白布肃续。 她就那樣靜靜地躺著黍檩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪始锚。 梳的紋絲不亂的頭發(fā)上刽酱,一...
    開封第一講書人閱讀 52,184評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音瞧捌,去河邊找鬼棵里。 笑死润文,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殿怜。 我是一名探鬼主播典蝌,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼头谜!你這毒婦竟也來了骏掀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤柱告,失蹤者是張志新(化名)和其女友劉穎截驮,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體际度,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葵袭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了乖菱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坡锡。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖窒所,靈堂內(nèi)的尸體忽然破棺而出鹉勒,到底是詐尸還是另有隱情,我是刑警寧澤吵取,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布贸弥,位于F島的核電站,受9級(jí)特大地震影響海渊,放射性物質(zhì)發(fā)生泄漏绵疲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一臣疑、第九天 我趴在偏房一處隱蔽的房頂上張望盔憨。 院中可真熱鬧,春花似錦讯沈、人聲如沸郁岩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽问慎。三九已至,卻和暖如春挤茄,著一層夾襖步出監(jiān)牢的瞬間如叼,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工穷劈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留笼恰,地道東北人踊沸。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像社证,于是被迫代替她去往敵國(guó)和親逼龟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • ANR基礎(chǔ)知識(shí) ANR全稱Application not response追葡,即程序無響應(yīng)腺律。 ANR的分類 KeyD...
    唐僧不愛洗頭_f7b5閱讀 3,282評(píng)論 0 12
  • ANR的類型 KeyDispatchTimeout(5 seconds) --主要類型按鍵或觸摸事件在特定時(shí)間內(nèi)無...
    codedreamzone閱讀 937評(píng)論 0 0
  • ANR問題疾渣,相信是每位開發(fā)日常都會(huì)遇到的問題,對(duì)于這類問題的分析崖飘,按照官方的推薦,或網(wǎng)絡(luò)博客的總結(jié)思路能解決一定的...
    tiger桂閱讀 17,942評(píng)論 5 28
  • 1杈女、ANR是什么朱浴?ANR英文全稱Application Not Responding,通俗來說就是程序未響應(yīng)达椰!如果...
    __帝華閱讀 1,019評(píng)論 0 0
  • 《做啰劲,就對(duì)了》 作者:(美國(guó))賴安·巴賓諾(Ryan Babineaux) (美國(guó))約翰·克朗伯茲(John Kr...
    漁山樵水閱讀 1,406評(píng)論 1 2