Android ANR分析

什么是ANR

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

發(fā)生ANR的場景

  • Service超時

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

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


我們從startService開始分析Service超時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)行一些簡單的檢查工作后,直接調(diào)用ActiveServices對象的startServiceLocked,進(jìn)一步調(diào)用bringUpServiceLocked來啟動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)程,首先要創(chuàng)建一個進(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è)置超時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ù)是前臺服務(wù)還是后臺服務(wù),bumpServiceExecutingLocked會根據(jù)服務(wù)的類型設(shè)定不同的超時時間,其中前臺服務(wù)的超時時間為SERVICE_TIMEOUT(20s),后臺服務(wù)的超時時間為10 * SERVICE_TIMEOUT。如果超時時間結(jié)束時,超時消息仍然沒有被移除,就表明有Service執(zhí)行超時,下面分析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í)行超時的服務(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í)行超時的服務(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 {
                // 沒有單個服務(wù)執(zhí)行超時,繼續(xù)設(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í)行超時的服務(wù),然后調(diào)用AppErrors的appNotResponding處理。


上面的整個過程就是appNotResponding的處理流程,對于細(xì)節(jié)我們在分析ANR一節(jié)中再討論,需要注意的
是紅色區(qū)域的流程在Android N上已經(jīng)改變(改善系統(tǒng)性能),詳情參考下面鏈接。

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

  • Broadcast超時

在分析Broadcast ANR之前我們先簡單了解下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有兩種注冊方式:

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ī)定的時間內(nèi)沒有收到廣播接收機(jī)
發(fā)送的finishReceiver的消息,就會觸發(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)注冊的廣播接收機(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 {
                // 查找動態(tài)注冊的廣播接收機(jī)
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false, userId);
            }
        }
        ...
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        if (!ordered && NR > 0) {
            // 發(fā)送普通廣播到動態(tài)注冊的廣播接收機(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ì)列,前臺廣播由前臺廣播對表處理;
            // 后臺廣播由后臺廣播隊(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) {
                // 將(前臺\后臺)普通廣播放入(前臺\后臺)并行廣播列表中
                queue.enqueueParallelBroadcastLocked(r);
                // 處理(前臺\后臺)并行廣播列表中的廣播
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }

        // Merge into one list.
        // 動態(tài)注冊的廣播接收機(jī)、靜態(tài)注冊的廣播接收機(jī)按優(yōu)先級排序(高->低),
        // 存放到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ì)列,前臺廣播由前臺廣播對表處理;
            // 后臺廣播由后臺廣播隊(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) {
                // 將(前臺\后臺)普通廣播放入(前臺\后臺)有序廣播列表中
                queue.enqueueOrderedBroadcastLocked(r);
                // 處理(前臺\后臺)有序廣播列表中的廣播
                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只是簡單的發(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);
                    //將廣播同時發(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)先級一個一個的分發(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之后的超時檢測
                    // 由于SystemReady之前的廣播發(fā)送可能很慢,而且不檢測,所以超時時間為
                    // 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");
                    // 一個廣播的所有接收機(jī)發(fā)送完成,取消超時消息設(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...
            // 獲取廣播的下一個接收者(可能有多個)發(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) {
                // 廣播多個接收者中的第一個,記錄分發(fā)時間
                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è)定
                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);
                // 如果是動態(tài)注冊的廣播接收機(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é)一下廣播的發(fā)送超時流程:


與服務(wù)超時ANR類似,如果在規(guī)定的時間內(nèi),廣播超時消息沒有取消,就會觸發(fā)ANR。接下來分析
廣播超時ANR的處理。

    final void broadcastTimeoutLocked(boolean fromMsg) {
        // fromMsg標(biāo)記超時觸發(fā)者,true表示超時消息觸發(fā)
        // false表示直接調(diào)用超時處理
        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ī)(可能多個)沒有超時,則重新設(shè)定超時消息;從這里
            // 看出超時其實(shí)是針對單個廣播接收機(jī),如果多個廣播接收機(jī)收發(fā)累計(jì)時間
            // 超時,并不會觸發(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ā)廣播超時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í)廣播超時ANR,這里要注意如果有dex2oat,廣播超時時間被推遲;如果系統(tǒng)啟動還未就緒,不檢測廣播超時。廣播超時ANR的處理流程跟服務(wù)超時ANR類似,不再贅述。

  • ContentProvider超時

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超時的示意圖如上圖所示,如果我們只是使用getContentResolver返回的對象(ApplicationContentResolver)來訪問ContentProvider提供的數(shù)據(jù),并不會有超時ANR的檢測。當(dāng)使用ContentResolverClient來訪問數(shù)據(jù)才會有超時ANR檢測。ContentResolverClient檢測到數(shù)據(jù)操作超時后,最終通過Binder IPC通知AMS,AMS的處理與之前討論的類似,不再贅述。

  • Input Dispatching超時

我們以Touch事件來說明Input Dispatching超時機(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) {
        // 對于按鍵事件處理,如果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)備好的時間,并計(jì)算出超時時間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對于Input Dispatching超時ANR的處理與之前類似,不再贅述。

如何分析ANR

  • 問題描述

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

  • Log分析

    • ANR發(fā)生的準(zhǔn)確時間及原因
    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ā)生時系統(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
    
    • 超時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的窗口,而是一直有輸入時間分發(fā)給ScrollCaptureUiService。繼續(xù)看發(fā)現(xiàn)實(shí)際上是ScrollCaptureUiService窗口不響應(yīng)輸入事件(根據(jù)waitQueue中事件數(shù)量以及事件超時時間可判定),為什么ScrollCaptureUiService不響應(yīng)輸入事件?繼續(xù)看log發(fā)現(xiàn)在收到輸入事件的時候,com.samsung.android.app.scrollcapture發(fā)生Crash,這就可以解釋ScrollCaptureUiService窗口不響應(yīng)輸入事件啦(PS:細(xì)節(jié)后續(xù)討論debuggerd64時候再展開)。

    ...
    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)用之上。對于窗口與應(yīng)用handle不一致的情況,發(fā)生ANR時,首先找到窗口handle也就是Window{8d82752 u0 d0 p7647 ScrollCaptureUiService}對應(yīng)的應(yīng)用,如果為空的話,系統(tǒng)認(rèn)為應(yīng)用handle對應(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)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評論 2 374

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

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