ANR的原理分析和簡單總結


前言

ANR(Application Not Responding),應用無響應,這個可能每個人都碰到過。

該篇主要簡單總結下,ANR的幾種常見類型(輸入事件、廣播接收器、Service、ContentProvider),以及ANR一般如何產生的及如何避免。

最后重點是通過源碼 了解這幾種類型 是如何產生ANR、超時時間是怎么來的、ANR后如何處理的等。

關於 ANR發生后如何分析和處理 這個不在該篇總結,會另起一篇 盡可能比較詳細的說明。

ANR簡述

幾種ANR類型簡單總結

幾種ANR簡單用表格列出下,下面幾種情況都會造成ANR:

ANR類型 超時時間 報錯信息
輸入事件(按鍵、觸摸等) 5s Input event dispatching timed out
廣播BroadcastReceiver 前台10s,后台/offload 60s Receiver during timeout of
Service服務 Foreground 10s,普通 20s,后台 200s Timeout executing service
ContentProvider 10s timeout publishing content providers

如上述表格所述,列出了4種ANR的 超時時間以及報錯信息。

在后面,有這 四種會引起ANR類型 源碼分析(該篇重點),能夠看到超時時間是如何來的,報錯如何產生的,產出ANR后是如何處理的(除ContentProvider外,其他都會調用ProcessRecord.appNotResponding())。

注:ContentProvider算不算ANR類型,個人理解 算是,但與其他幾種比有點區別。


ANR的產生

ANR的產生就是超時。

長時間無法響應用戶輸入 或 無效占用資源,會使用戶感覺卡頓的感覺。一般這種情況,會彈出對話框提示用戶,可以用來選擇關閉應用。

這個超時,由 AMS和WMS檢測(后面的源碼分析可以看到,官網 上也有相關說明:AMS和WMS監控應用的響應性),未在規定時間完成特定任務(如5s未響應輸入時間、10s內廣播接收器未處理完畢),即會引起ANR。

這個超時,一般由Handler機制 的 延遲發送消息完成。若超時 則發出消息 產生ANR 告知系統及用戶,若在時間內完成,則取消消息隊列中的延遲消息。

為什么會導致超時,或者導致ANR?

UI線程(主線程) 阻塞:

如在主線程存取較多數據(I/O阻塞),網絡阻塞等。也可能是資源 不足造成,如CPU負荷、內存或存儲不足等導致I/O阻塞。

死鎖和等待:

多線程的死鎖和主線程的等待。

ANR的避免

有上面的可能可能原因,可以針對原因進行一些避免。

避免主線程阻塞:

UI線程盡量只做跟UI相關的工作,使用子線程(工作線程) 處理耗時操作或阻塞任務(如數據庫、網絡、I/O操作);

盡量用Handler來處理UI線程和其他線程之間的交互;

各大組件生命周期中也應避免耗時操作,如Activity的onCreate()、BroadcastReciever的onRecieve()等。廣播如有耗時操作,建議放到IntentService中執行。

注: IntentService繼承了Service,運行在后台線程中,處理異步需求,工作執行完畢后自動銷毀。

但Android版本8.0開始限制后台,API level 30被棄用。8.0及更高版本 建議使用WorkManager 或 JobIntentService替代。

避免CPU負荷或I/O阻塞:

文件或數據操作放到子線程,異步方式完成。

ANR發生后的信息收集

一般ANR發生后 系統會調用appNotResponding()收集信息。

ANR發生后,CPU的負載情況、IOWait、traces文件(/data/anr/traces.txt)都會保存下來,導出log和traces文件進行進一步分析。

不同soc的 log會有不同,注意獲取完整的log和traces文件。(如MTK平台,會生成aee_exp文件夾,其所有log也會保存在mtklog文件夾,Q版本之后mtklog變為了debuglogger)

至於 發生ANR錯誤 如何進行分析並解決,該篇不贅述,計划另起一篇比較詳細的總結。

ANR源碼分析

下面源碼不復雜,主要簡單了解下 ANR是如何產生 的。從這個過程,能夠看到超時時間是如何來的,報錯如何產生的,產出ANR后是如何處理的。

源碼也是基於Android10的。

輸入事件

Android10_原理機制系列_事件傳遞機制 中,個人感覺 對Android的事件傳遞過程總結的應該還比較清晰。不過事件傳遞在Native層過程 很復雜,考慮的狀況多,那篇也只看的傳遞過程的主線。

記得在事件傳遞機制這篇中,也mark了個ANR的相關點(resetANRTimeoutsLocked()),當時也沒細看。這里簡單看下輸入事件的ANR過程。直接列出 相關源碼:

//InputDispatcher.cpp
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
        const MotionEntry* entry, std::vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
        bool* outConflictingPointerActions) {
    injectionResult = handleTargetsNotReadyLocked(currentTime, entry, ...);        
}

int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, ...) {
    if(...) {
        mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY;
    } else {
        mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY;
    }
    if (currentTime >= mInputTargetWaitTimeoutTime) {
        onANRLocked(currentTime, applicationHandle, windowHandle,
                entry->eventTime, mInputTargetWaitStartTime, reason);
        return INPUT_EVENT_INJECTION_PENDING;
    } 
}

void InputDispatcher::onANRLocked(
        nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle,
        nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) {
    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyANRLockedInterruptible);
}

void InputDispatcher::doNotifyANRLockedInterruptible(
        CommandEntry* commandEntry) {
    mLock.unlock();
    nsecs_t newTimeout = mPolicy->notifyANR(
            commandEntry->inputApplicationHandle,
            commandEntry->inputChannel ? commandEntry->inputChannel->getToken() : nullptr,
            commandEntry->reason);

    mLock.lock();
    resumeAfterTargetsNotReadyTimeoutLocked(newTimeout,
            commandEntry->inputChannel);
}

接着直接看到mPolicy->notifyANR(),在 Android10_原理機制系列_事件傳遞機制 中,mPolicy是什么,如何回到java層的都說的比較詳細了,這里直接列出回到java層過程的源碼:

//com_android_server_input_InputManagerService.cpp
nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle,
        const sp<IBinder>& token, const std::string& reason) {
    jlong newTimeout = env->CallLongMethod(mServiceObj,
                gServiceClassInfo.notifyANR, tokenObj,
                reasonObj);
    if (checkAndClearExceptionFromCallback(env, "notifyANR")) {
        newTimeout = 0; // abort dispatch
    } else {
        assert(newTimeout >= 0);
    }
    return newTimeout;
}

//InputManagerService.java
private long notifyANR(IBinder token, String reason) {
    return mWindowManagerCallbacks.notifyANR(
            token, reason);
}

回到了IMS中,接着就直接看了。

//InputManagerCallback.java
@Override
public long notifyANR(IBinder token, String reason) {
    //輸入事件log打印,不同情況打印不一樣。不過頭部都是Input event dispatching timed out
    synchronized (mService.mGlobalLock) {
            Slog.i(TAG_WM, "Input event dispatching timed out "
                    + ...);
    }
    
    ...
    } else if (windowState != null) {
        // Notify the activity manager about the timeout and let it decide whether
        // to abort dispatching or keep waiting.
        long timeout = mService.mAmInternal.inputDispatchingTimedOut(
                windowState.mSession.mPid, aboveSystem, reason);
        if (timeout >= 0) {
            // The activity manager declined to abort dispatching.
            // Wait a bit longer and timeout again later.
            return timeout * 1000000L; // nanoseconds
        }
    }
    return 0; // abort dispatching
}

//ActivityManagerService.java
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal {
    @Override
    public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
        synchronized (ActivityManagerService.this) {
            return ActivityManagerService.this.inputDispatchingTimedOut(
                    pid, aboveSystem, reason);
        }
    }
}

long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
    synchronized (this) {
        timeout = proc != null ? proc.getInputDispatchingTimeout() : KEY_DISPATCHING_TIMEOUT_MS;
    }
    if (inputDispatchingTimedOut(proc, null, null, null, null, aboveSystem, reason)) {
        return -1;
    }
    return timeout;
}

boolean inputDispatchingTimedOut(ProcessRecord proc, String activityShortComponentName,
        ApplicationInfo aInfo, String parentShortComponentName,
        WindowProcessController parentProcess, boolean aboveSystem, String reason) {
    if (proc != null) {
        proc.appNotResponding(activityShortComponentName, aInfo,
                parentShortComponentName, parentProcess, aboveSystem, annotation);
    }

    return true;
}

很明顯,上面可以看到ANR超時后,會調用appNotResponding()。

超時時間是多少呢?

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

//ActivityTaskManagerService.java
// How long we wait until we timeout on key dispatching.
public static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
// How long we wait until we timeout on key dispatching during instrumentation.
static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;

//ProcessRecord.java
public long getInputDispatchingTimeout() {
    return mWindowProcessController.getInputDispatchingTimeout();
}
//WindowProcessController.java
public long getInputDispatchingTimeout() {
    synchronized (mAtm.mGlobalLock) {
        return isInstrumenting() || isUsingWrapper()
                ? INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS : KEY_DISPATCHING_TIMEOUT_MS;
    }
}

很明顯,正常情況下 超時事件為 5s。

BroadcastReceiver

簡單的從sendBroadcast()開始看一下:

//ContextImpl.java
@Override
public void sendBroadcast(Intent intent) {
    try {
        ActivityManager.getService().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                getUserId());
    }
}

//ActivityManagerService.java
@GuardedBy("this")
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 realCallingUid,
        int realCallingPid, int userId) {
    return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
        resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
        sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
        false /* allowBackgroundActivityStarts */);
}

@GuardedBy("this")
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 realCallingUid,
        int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
    intent = new Intent(intent);
    // Figure out who all will receive this broadcast.
    List receivers = null;
    List<BroadcastFilter> registeredReceivers = null;
    ...

    if ((receivers != null && receivers.size() > 0)
            || resultTo != null) {
        BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                resultData, resultExtras, ordered, sticky, false, userId,
                allowBackgroundActivityStarts, timeoutExempt);
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
        final BroadcastRecord oldRecord =
                replacePending ? queue.replaceOrderedBroadcastLocked(r) : null;
        if (oldRecord != null) {
            ...
        } else {
            //加入到mOrderedBroadcasts隊列中
            queue.enqueueOrderedBroadcastLocked(r);
            //這里接着往下看
            queue.scheduleBroadcastsLocked();
        }
    }
    return ActivityManager.BROADCAST_SUCCESS;
}

從sendBroadcast() 到broadcastIntentLocked() ,broadcastIntentLocked() 方法很長,對不同廣播進行了不同處理,接着直接看 queue.scheduleBroadcastsLocked() 是如何處理的。

//BroadcastQueue.java
public void scheduleBroadcastsLocked() {
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

private final class BroadcastHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_INTENT_MSG: {
                if (DEBUG_BROADCAST) Slog.v(
                        TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
                        + mQueueName + "]");
                processNextBroadcast(true);
            } break;
            case BROADCAST_TIMEOUT_MSG: {
                synchronized (mService) {
                    broadcastTimeoutLocked(true);
                }
            } break;
        }
    }
}

final void processNextBroadcast(boolean fromMsg) {
    synchronized (mService) {
        processNextBroadcastLocked(fromMsg, false);
    }
}

final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    r.receiverTime = SystemClock.uptimeMillis();
    if (! mPendingBroadcastTimeoutMessage) {
        //當前時間戳+超時時間(mConstants.TIMEOUT)
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
                "Submitting BROADCAST_TIMEOUT_MSG ["
                + mQueueName + "] for " + r + " at " + timeoutTime);
        setBroadcastTimeoutLocked(timeoutTime);
    }
    ...
}

final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        //延遲發送BROADCAST_TIMEOUT_MSG消息。 回到上面看到,接收處理:broadcastTimeoutLocked(true);
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

final void broadcastTimeoutLocked(boolean fromMsg) {
    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
    if (fromMsg) {
        //應該完成的時間
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        //未超時
        if (timeoutTime > now) {
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }
    final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
    r.receiverTime = now;
    if (!debugging) {
        r.anrCount++;
    }
    ProcessRecord app = null;
    String anrMessage = null;
    Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
    ...
    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }
    if (!debugging && 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));
    }
}

scheduleBroadcastsLocked() 通過handler最終執行了 processNextBroadcast()->processNextBroadcastLocked(),其中設置了超時時間 r.receiverTime + mConstants.TIMEOUT; 最終通過 setBroadcastTimeoutLocked()->broadcastTimeoutLocked() 處理判斷是否超時,生成ANR,最終也是調用的appNotResponding()。

這個超時(mConstants.TIMEOUT) 時間到底是多少呢?

現在具體看下:

//BroadcastQueue.java
final BroadcastConstants mConstants;
BroadcastQueue(ActivityManagerService service, Handler handler,
               String name, BroadcastConstants constants, boolean allowDelayBehindServices) {
    ...
    mConstants = constants;
}

在構造BroadcastQueue時 傳入的,這個即AMS調用時 queue.scheduleBroadcastsLocked(); 中的queue

BroadcastQueue queue = broadcastQueueForIntent(intent); 下面來看下這個方法:

//ActivityManagerService.java
BroadcastQueue broadcastQueueForIntent(Intent intent) {
    if (isOnOffloadQueue(intent.getFlags())) {
        return mOffloadBroadcastQueue;
    }
    final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
    return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
}

mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
                                       "foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
                                       "background", backConstants, true);
mOffloadBroadcastQueue = new BroadcastQueue(this, mHandler,
                                            "offload", offloadConstants, true);

看下定義,這個超時時間即在foreConstants、backConstants、offloadConstants中,在看定義:

//ActivityManagerService.java
// Broadcast policy parameters
final BroadcastConstants foreConstants = new BroadcastConstants(
    Settings.Global.BROADCAST_FG_CONSTANTS);
foreConstants.TIMEOUT = BROADCAST_FG_TIMEOUT;

final BroadcastConstants backConstants = new BroadcastConstants(
    Settings.Global.BROADCAST_BG_CONSTANTS);
backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;

final BroadcastConstants offloadConstants = new BroadcastConstants(
    Settings.Global.BROADCAST_OFFLOAD_CONSTANTS);
offloadConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
// by default, no "slow" policy in this queue
offloadConstants.SLOW_TIME = Integer.MAX_VALUE;

// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

同樣,不同類型廣播 超時時間也是不一樣的。 所以很清晰看到:前台廣播超時時間10s,后台廣播和offload類型,超時是60s。

Service

在總結四大組件中已經說過,service的啟動有兩種方式:startService()和bindService() 。這里主要看下startService()的流程,ANR是如何產生的。

//ContextImpl.java
@Override
public ComponentName startService(Intent service) {
    return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
        UserHandle user) {
    try {
        ComponentName cn = ActivityManager.getService().startService(
            mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                        getContentResolver()), requireForeground,
                        getOpPackageName(), user.getIdentifier());
        return cn;
    }
}

//ActivityManagerService.java
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
        String resolvedType, boolean requireForeground, String callingPackage, int userId)
        throws TransactionTooLargeException {
        res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, userId);
        return res;
    }
}

//ActiveServices.java
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
        throws TransactionTooLargeException {
    return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
            callingPackage, userId, false);
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
        int callingPid, int callingUid, boolean fgRequired, String callingPackage,
        final int userId, boolean allowBackgroundActivityStarts)
        throws TransactionTooLargeException {
    ServiceLookupResult res =
        retrieveServiceLocked(service, null, resolvedType, callingPackage,
                callingPid, callingUid, userId, true, callerFg, false, false);
    ServiceRecord r = res.record;
    ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
    return cmp;
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
        boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
    return r.name;
}
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting, boolean permissionsReviewRequired)
        throws TransactionTooLargeException {
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                realStartServiceLocked(r, app, execInFg);
                return null;
            }
        }
    }
    return null;
}

很簡單清晰,不贅述,這里最終可以看到 走到了realStartServiceLocked()方法,由方法名可以猜測是 真正啟動服務的關鍵,下面繼續看,這里如何啟動服務的不關注,主要看ANR的產生。

//ActiveServices.java
/**
 * Note the name of this method should not be confused with the started services concept.
 * The "start" here means bring up the instance in the client, and this method is called
 * from bindService() as well.
 */
private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    r.setProcess(app);
    r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
    final boolean newService = app.services.add(r);
    //真正創建服務前執行的
    bumpServiceExecutingLocked(r, execInFg, "create");
    ...
    app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.getReportedProcState());
    ...
    serviceDoneExecutingLocked(r, inDestroying, inDestroying);
}

從注釋也能看到,bindService() 也會調用。這里繼續看bumpServiceExecutingLocked():

//ActiveServices.java
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    scheduleServiceTimeoutLocked(r.app);
    ...
    r.executeFg |= fg;
    r.executeNesting++;
    //r.executingStart記錄了創建服務開始的時間戳
    r.executingStart = now;
}

void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    //延遲發送,即超時時間
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

//ActivityManagerService.java
final class MainHandler extends Handler {
    public MainHandler(Looper looper) {
        super(looper, null, true);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case SERVICE_TIMEOUT_MSG: {
            mServices.serviceTimeout((ProcessRecord)msg.obj);
        } break;
        case SERVICE_FOREGROUND_TIMEOUT_MSG: {
            mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
        } break;
        }
    }
}

//ActiveServices.java
void serviceTimeout(ProcessRecord proc) {
    String anrMessage = null;
    synchronized(mAm) {
        if (proc.executingServices.size() == 0 || proc.thread == null) {
            return;
        }
        final long now = SystemClock.uptimeMillis();
        //當前時間 - timeout時間,即最晚開始時間
        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--) {
            ServiceRecord sr = proc.executingServices.valueAt(i);
            //開始時間 小於 最晚開始時間,即超時了
            if (sr.executingStart < maxTime) {
                timeout = sr;
                break;
            }
            if (sr.executingStart > nextTime) {
                nextTime = sr.executingStart;
            }
        }
        if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) {
            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);
            //超時 記錄的信息,即ANR信息
            anrMessage = "executing service " + timeout.shortInstanceName;
        } else {
            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) {
        //ANR信息傳遞到進程 處理。
        proc.appNotResponding(null, null, null, null, false, anrMessage);
    }
}

仔細看下上面代碼及注釋 就能理解。最終超時信息傳入到進程進行處理。 proc.appNotResponding(null, null, null, null, false, anrMessage);

這個超時時間定義如下,注釋也很清晰,不同類型服務 超時時間是不一樣的。

//ActiveServices.java
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

ContentProvider

在應用啟動時,ContentProvider發布 若超時也會發生ANR。

應用啟動后,ActivityThread執行attach()操作,最后會執行attachApplicationLocked() 實現上述ANR判斷。

//ActivityManagerService.java
// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
/**
 * How long we wait for an provider to be published. Should be longer than
 * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
 */
static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;

@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
        int pid, int callingUid, long startSeq) {
    if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
        Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
        msg.obj = app;
        //可能ANR
        mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
    }         
    ...
    try {
        //移除CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG延遲消息
        //這里的thread是從ActivityThread傳入的,ApplicationThread對象。
        thread.bindApplication(processName, appInfo, providers, ...);
    }
}


final class MainHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
        case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
            ProcessRecord app = (ProcessRecord)msg.obj;
            synchronized (ActivityManagerService.this) {
                processContentProviderPublishTimedOutLocked(app);
            }
        } break;
        }
    }
}

@GuardedBy("this")
private final void processContentProviderPublishTimedOutLocked(ProcessRecord app) {
    cleanupAppInLaunchingProvidersLocked(app, true);
    mProcessList.removeProcessLocked(app, false, true, "timeout publishing content providers");
}

CONTENT_PROVIDER_PUBLISH_TIMEOUT是10s,10s后發送消息 Hander進行處理,會有timeout publishing content providers信息。在10s內完成,會移除該消息,即不會觸發ANR了。

若發生超時,這里沒有調用appNotResponding()(不像前3種),這里會殺掉進程並清理了相關信息。

如何移除,看thread.bindApplication(),該方法在延遲發送消息之后執行,即移除延遲消息。如在10s內執行完成,就是不會觸發ANR。

注:這是最簡單直接看到的一種,移除該消息的調用地方不只一處。

簡單看下這個移除延遲消息過程:

//ActivityThread.java
private class ApplicationThread extends IApplicationThread.Stub {
    public final void bindApplication(String processName, ApplicationInfo appInfo,
            ...) {
        sendMessage(H.BIND_APPLICATION, data);
    }
}

class H extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BIND_APPLICATION:
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);
                break;
        }
    }
}

@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
    try {
        if (!data.restrictedBackupMode) {
            if (!ArrayUtils.isEmpty(data.providers)) {
                installContentProviders(app, data.providers);
            }
        }
    } 
}

@UnsupportedAppUsage
private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    try {
        ActivityManager.getService().publishContentProviders(
            getApplicationThread(), results);
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

//ActivityManagerService.java
public final void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) {
    final ProcessRecord r = getRecordForAppLocked(caller);
    if (wasInLaunchingProviders) {
        mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, r);
    }  
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM