1. 程式人生 > >android ANR原始碼分析 --- 之一

android ANR原始碼分析 --- 之一

概述:

ANR(ApplicationNot responding),是指應用程式未響應,Android系統對於一些事件需要在一定的時間範圍內完成,如果超過預定時間

能未能得到有效響應或者響應時間過長,都會造成ANR。一般地,這時往往會彈出一個提示框,告知使用者當前xxx未響應,使用者可選擇

繼續等待或者Force Close。

那麼哪些場景會造成ANR呢?

1,Service Timeout:服務在20s內未執行完成;

2,BroadcastQueueTimeout:比如前臺廣播在10s內執行完成

3,ContentProviderTimeout:內容提供者執行超時

4,inputDispatchingTimeout: 輸入事件分發超時5s,包括按鍵分發事件的超時。

1, Service Timeout

ServiceTimeout觸發時機,簡單說就是AMS中的mHandler收到SERVICE_TIMEOUT_MSG訊息時觸發。

在service啟動時,最後會呼叫system_server程序中的ActiveServices的attachApplicationLocked方法,然後呼叫realStartServiceLocked方法完成服務的啟動。

整個過程主要分為3個部分:

1,在啟動service之前傳送SERVICE_TIMEOUT_MSG 訊息;

2,如果service順利啟動,則刪除SERVICE_TIMEOUT_MSG 訊息;

3,如果SERVICE_TIMEOUT_MSG 未刪除,時間到後就會發生ANR。

1.1 傳送訊息

ActiveServices的realStartServiceLocked方法在呼叫service的OnCreate方法之前就會發送訊息,呼叫流程圖如下,


realStartServiceLocked方法呼叫如下,

bumpServiceExecutingLocked(r, execInFg, "create");//傳送訊息
•••
app.thread.scheduleCreateService(r, r.serviceInfo,
  mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo), app.repProcState);
//呼叫service的OnCreate方法

在bumpServiceExecutingLocked方法中回撥用scheduleServiceTimeoutLocked方法傳送SERVICE_TIMEOUT_MSG 訊息。

scheduleServiceTimeoutLocked方法如下,

long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));

首先獲取系統的當前時間,然後利用AMS的mHandler變數構造SERVICE_TIMEOUT_MSG訊息,最後呼叫AMS的mHandler變數的sendMessageAtTime方法傳送訊息。

SERVICE_TIMEOUT和SERVICE_BACKGROUND_TIMEOUT定義如下,

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

因此, 對於前臺服務,則超時為SERVICE_TIMEOUT,即timeout=20s;

對於後臺服務,則超時為SERVICE_BACKGROUND_TIMEOUT,即timeout=200s;

1.2 刪除訊息

在比較多的地方會刪除訊息,

比如,在realStartServiceLocked方法中

app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);

呼叫service的OnCreate方法時程序掛掉之後會呼叫serviceDoneExecutingLocked方法刪除訊息。

catch (DeadObjectException e) {
    Slog.w(TAG, "Application dead when creating service " + r);
    mAm.appDiedLocked(app);
    throw e;
} finally {
    if (!created) {
        // Keep the executeNesting count accurate.
        final boolean inDestroying = mDestroyingServices.contains(r);
        serviceDoneExecutingLocked(r, inDestroying, inDestroying);

或者在sendServiceArgsLocked方法中,啟動service丟擲異常也會呼叫serviceDoneExecutingLocked方法刪除訊息。

if (caughtException != null) {
    // Keep nesting count correct
    final boolean inDestroying = mDestroyingServices.contains(r);
    serviceDoneExecutingLocked(r, inDestroying, inDestroying);
app.thread.scheduleCreateService(r, r.serviceInfo, mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);

此處會呼叫ActivityThread的內部類ApplicationThread的scheduleCreateService方法,最後會呼叫handleCreateService方法。

ActivityThread的handleCreateService方法主要邏輯如下,

1,載入service 物件,

LoadedApk packageInfo = getPackageInfoNoCheck(data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
     java.lang.ClassLoader cl = packageInfo.getClassLoader();
     service = (Service) cl.loadClass(data.info.name).newInstance();

2,呼叫service的onCreate方法,

Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);

3,成功呼叫service的onCreate方法之後,跨程序呼叫AMS的serviceDoneExecuting方法刪除訊息。

try {
   ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);

AMS的serviceDoneExecuting方法如下,

mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);

直接呼叫ActiveServices的serviceDoneExecutingLocked方法.

由此可見,刪除SERVICE_TIMEOUT_MSG 訊息一般通過ActiveServices的serviceDoneExecutingLocked方法。

serviceDoneExecutingLocked方法邏輯如下,

if (r.app.executingServices.size() == 0) {
    if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
                            "No more executingServices of " + r.shortName);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);

如果當前服務所在程序中沒有正在執行的service,就刪除SERVICE_TIMEOUT_MSG訊息。

1.3 處理訊息

AMS中內部類MainHandler的handleMessage方法對SERVICE_TIMEOUT_MSG訊息處理如下,

mServices.serviceTimeout((ProcessRecord)msg.obj);

呼叫ActiveServices的serviceTimeout方法進行處理,

serviceTimeout方法邏輯如下,

if (anrMessage != null) {
    mAm.appNotResponding(proc, null, null, false, anrMessage);
}

呼叫AMS的appNotResponding方法統一處理。處理過程後面詳細論述。

其中, anrMessage 字串為 executing service [傳送超時serviceRecord資訊]