android ANR優(yōu)化

本文將從介紹什么是ANR,給出anr產生的幾種觸發(fā)點,分析這幾種情況下是怎么產生anr的鞭呕,然后給出優(yōu)化的方法這幾個方面進行討論赏淌。

1.什么是ANR

ANR定義:在Android上靶擦,如果你的應用程序有一段時間響應不夠靈敏,系統(tǒng)會向用戶顯示一個對話框忘巧,這個對話框稱作應用程序無響應(ANR:Application Not Responding)對話框。

大致的原因就是在特定的時間沒有完成任務

它是通過ActivityManager和WindowManager系統(tǒng)服務監(jiān)視的睦刃。

細分可以分從兩部分分:

  • 1.內部:阻塞了主進程砚嘴,IOwait等
  • 2.外部:當前應用進程調用請求進程,其他進程長時間無反饋涩拙。
    其他進程的CPU占用率高际长,使當前應用無法搶占CPU時間片。

2.ANR觸發(fā)點

  1. Service TimeOut:前臺服務20s兴泥,后臺200s
  2. BroadcastReceiver TimeOut : 前臺廣播10s后臺廣播20s
  3. ContentProvider TimeOut:內容提供者不超過10s
  4. 按鍵或觸發(fā)時間超時:5s

3.ANR觸發(fā)點原理分析

3.1. Service Timeout

Service Timeout觸發(fā)時機工育,簡單說就是AMS中的mHandler收到SERVICE_TIMEOUT_MSG消息時觸發(fā)。

那我們需要知道這個消息是什么時候開始初始的傅事,如果沒有timeout是怎么處理這個消息的凡泣。timeout又做了什么操作。

Service startService開始一個service最終都會調用ActiveServices.java的realStartServiceLocked方法

 private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    ...
    //A.發(fā)送delay消息(SERVICE_TIMEOUT_MSG)初始化
    bumpServiceExecutingLocked(r, execInFg, "create");
    try {
        ...
        //最終執(zhí)行服務的onCreate()方法
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);

        } catch (DeadObjectException e) {
            ...
    } finally {
        if (!created) {
            //B.當service啟動完畢瘩将,則remove SERVICE_TIMEOUT_MSG消息
            serviceDoneExecutingLocked(r, inDestroying, inDestroying);
            ...
        }
    }
}

流程為:首先發(fā)送一個超時消息怔接,然后出來onCrete(),最后如果創(chuàng)建成功結束消息

A.bumpServiceExecutingLocked最終調用到延時發(fā)送消息流程如下:

初始化超時消息

這里設置了前臺和后臺超時的時間

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

B.service啟動完畢調用serviceDoneExecutingLocked結束消息

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing) {
    ...
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                //當前服務所在進程中沒有正在執(zhí)行的service
                mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
        ...
    }
    ...
}

C.AMS中會處理SERVICE_TIMEOUT_MSG (如果產生了超時)

final class MainHandler extends Handler {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SERVICE_TIMEOUT_MSG: {
                ...
                mServices.serviceTimeout((ProcessRecord)msg.obj);
            } break;
            ...
        }
        ...
    }
}

D.ActivityService.java 調用serviceTimeout(最終調用 appNotResponding)

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--) {
                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)) {
                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 {
                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) {
            mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
        }
    }

3.2 BroadcastQueue Timeout

廣播機制是用于進程/線程間通信的搪泳。分兩類:

  • 靜態(tài)廣播接收者(AndroidManifest.xml標簽定義的)
  • 動態(tài)廣播接收者(通過AMS.registerReceiver())

廣播最終存放在如下隊列中:
mParallelBroadcasts(并行隊列)mOrderedBroadcasts(串行隊列)

類型 方法 ordered sticky
普通廣播 sendBroadcast false false
有序廣播 sendOrderedBroadcast true false
Sticky廣播 sendStickyBroadcast false true

廣播的發(fā)送方式:

類型 方法 ordered sticky
普通廣播 sendBroadcast false false
有序廣播 sendOrderedBroadcast true false
Sticky廣播 sendStickyBroadcast false true
  • 當發(fā)送串行廣播(ordered=true)的情況下:
  1. 靜態(tài)注冊的廣播接收者(receivers),采用串行處理
  2. 動態(tài)注冊的廣播接收者(registeredReceivers)蜕提,采用串行處理
  • 當發(fā)送并行廣播(ordered=false)的情況下:
  1. 靜態(tài)注冊的廣播接收者(receivers)森书,依然采用串行處理
  2. 動態(tài)注冊的廣播接收者(registeredReceivers),采用并行處理

為什么討論這個內容了谎势?

只有串行廣播才需要考慮超時凛膏,因為接收者是串行處理的,前一個receiver處理慢脏榆,會影響后一個receiver猖毫;并行廣播 通過一個循環(huán)一次性向所有的receiver分發(fā)廣播事件,所以不存在彼此影響的問題须喂,則沒有廣播超時

簡單來說吁断,靜態(tài)注冊的receivers始終采用串行方式來處理(processNextBroadcast); 動態(tài)注冊的registeredReceivers處理方式是串行還是并行方式, 取決于廣播的發(fā)送方式(processNextBroadcast)坞生。

根據(jù)源碼分析:廣播最終是通過BroadcastQueue.java中的processNextBroadcast進行處理的仔役。

1.processNextBroadcast執(zhí)行過程分4步驟

  • A. 處理并行廣播
  • B. 處理當前有序廣播
  • C. 獲取下條有序廣播
  • D. 處理下條有序廣播
final void processNextBroadcast(boolean fromMsg) {
    synchronized(mService) {
    
        //A.處理并行廣播,忽略不需要處理超時
        ...
        //B.處理當前有序廣播
        do {
            r = mOrderedBroadcasts.get(0);
            //獲取所有該廣播所有的接收者
            int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
            if (mService.mProcessesReady && r.dispatchTime > 0) {
                long now = SystemClock.uptimeMillis();
                if ((numReceivers > 0) &&
                        (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                    //當廣播處理時間超時是己,則強制結束這條廣播
                    broadcastTimeoutLocked(false);
                    ...
                }
            }
            if (r.receivers == null || r.nextReceiver >= numReceivers
                    || r.resultAbort || forceReceive) {
                if (r.resultTo != null) {
                    //處理廣播消息消息
                    performReceiveLocked(r.callerApp, r.resultTo,
                        new Intent(r.intent), r.resultCode,
                        r.resultData, r.resultExtras, false, false, r.userId);
                    r.resultTo = null;
                }
                //取消BROADCAST_TIMEOUT_MSG消息
                cancelBroadcastTimeoutLocked();
            }
        } while (r == null);
        ...

        //C.獲取下條有序廣播
        r.receiverTime = SystemClock.uptimeMillis();
        if (!mPendingBroadcastTimeoutMessage) {
            long timeoutTime = r.receiverTime + mTimeoutPeriod;
            //設置廣播超時時間又兵,發(fā)送BROADCAST_TIMEOUT_MSG
            setBroadcastTimeoutLocked(timeoutTime);
        }
        ...
    }
}

廣播處理時機:

  • 1.在C的時候會setBroadcastTimeoutLocked(timeoutTime);
  • 2.在B串行處理的時候。
A.當廣播接收者等待超時卒废,這調用broadcastTimeoutLocked(false)
B.當處理結束的時候調用cancelBroadcastTimeoutLocked()

2.setBroadcastTimeoutLocked

final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (! mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

long timeoutTime = r.receiverTime + mTimeoutPeriod
mTimeoutPeriod中的時間是通過注冊的是時候是前臺廣播或后臺廣播決定的沛厨。

static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;

3.cancelBroadcastTimeoutLocked

final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false;
    }
}

4.BROADCAST_TIMEOUT_MSG處理

使用的內部定義的一個handle

private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
    }

5.broadcastTimeoutLocked

final void broadcastTimeoutLocked(boolean fromMsg) {
    if (fromMsg) {
        mPendingBroadcastTimeoutMessage = false;
    }

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

    long now = SystemClock.uptimeMillis();
    BroadcastRecord r = mOrderedBroadcasts.get(0);
    if (fromMsg) {
        if (mService.mDidDexOpt) {
            //延遲timeouts直到dexopt結束
            mService.mDidDexOpt = false;
            long timeoutTime = SystemClock.uptimeMillis() + mTimeoutPeriod;
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
        if (!mService.mProcessesReady) {
            //當系統(tǒng)還沒有準備就緒時摔认,廣播處理流程中不存在廣播超時
            return;
        }

        long timeoutTime = r.receiverTime + mTimeoutPeriod;
        if (timeoutTime > now) {
            //過早的timeout逆皮,重新設置廣播超時
            setBroadcastTimeoutLocked(timeoutTime);
            return;
        }
    }

    BroadcastRecord br = mOrderedBroadcasts.get(0);
    if (br.state == BroadcastRecord.WAITING_SERVICES) {
        //廣播已經處理完成,但需要等待已啟動service執(zhí)行完成参袱。當?shù)却銐驎r間电谣,則處理下一條廣播。
        br.curComponent = null;
        br.state = BroadcastRecord.IDLE;
        processNextBroadcast(false);
        return;
    }

    r.receiverTime = now;
    //當前BroadcastRecord的anr次數(shù)執(zhí)行加1操作
    r.anrCount++;

    if (r.nextReceiver <= 0) {
        return;
    }

    ProcessRecord app = null;
    String anrMessage = null;

    Object curReceiver = r.receivers.get(r.nextReceiver-1);
    //根據(jù)情況記錄廣播接收者丟棄的EventLog
    logBroadcastReceiverDiscardLocked(r);
    if (curReceiver instanceof BroadcastFilter) {
        BroadcastFilter bf = (BroadcastFilter)curReceiver;
        if (bf.receiverList.pid != 0
                && bf.receiverList.pid != ActivityManagerService.MY_PID) {
            synchronized (mService.mPidsSelfLocked) {
                app = mService.mPidsSelfLocked.get(
                        bf.receiverList.pid);
            }
        }
    } else {
        app = r.curApp;
    }

    if (app != null) {
        anrMessage = "Broadcast of " + r.intent.toString();
    }

    if (mPendingBroadcast == r) {
        mPendingBroadcast = null;
    }

    //繼續(xù)移動到下一個廣播接收者
    finishReceiverLocked(r, r.resultCode, r.resultData,
            r.resultExtras, r.resultAbort, false);
    scheduleBroadcastsLocked();

    if (anrMessage != null) {
        mHandler.post(new AppNotResponding(app, anrMessage));
    }
}
3.3 ContentProvider Timeout

AMS.appNotRespondingViaProvider

public void appNotRespondingViaProvider(IBinder connection) {
    enforceCallingPermission(
            android.Manifest.permission.REMOVE_TASKS, "appNotRespondingViaProvider()");

    final ContentProviderConnection conn = (ContentProviderConnection) connection;
    if (conn == null) {
        return;
    }

    final ProcessRecord host = conn.provider.proc;
    //無法找到provider所處的進程
    if (host == null) {
        return;
    }

    final long token = Binder.clearCallingIdentity();
    try {
        appNotResponding(host, null, null, false, "ContentProvider not responding");
    } finally {
        Binder.restoreCallingIdentity(token);
    }
}
ContentProviderClient.NotRespondingRunnable.run
    ContextImpl.ApplicationContentResolver.appNotRespondingViaProvider
        ActivityThread.appNotRespondingViaProvider
            AMP.appNotRespondingViaProvider
                AMS.appNotRespondingViaProvider

** 3.4 inputDispatching Timeout 和 keyDispatching Timeout**

觸發(fā)點在硬件

1.InputManagerService.java 觸發(fā)點在

// Native callback.
    private long notifyANR(InputApplicationHandle inputApplicationHandle,
            InputWindowHandle inputWindowHandle, String reason) {
        return mWindowManagerCallbacks.notifyANR(
                inputApplicationHandle, inputWindowHandle, reason);
    }
    
   // mWindowManagerCallbacks為InputMonitor對象

2.InputMonitor

public long notifyANR(InputApplicationHandle inputApplicationHandle,
        InputWindowHandle inputWindowHandle, String reason) {
    AppWindowToken appWindowToken = null;
    WindowState windowState = null;
    boolean aboveSystem = false;
    synchronized (mService.mWindowMap) {
        if (inputWindowHandle != null) {
            windowState = (WindowState) inputWindowHandle.windowState;
            if (windowState != null) {
                appWindowToken = windowState.mAppToken;
            }
        }
        if (appWindowToken == null && inputApplicationHandle != null) {
            appWindowToken = (AppWindowToken)inputApplicationHandle.appWindowToken;
        }
        //輸出input事件分發(fā)超時log
        if (windowState != null) {
            Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
                    + "sending to " + windowState.mAttrs.getTitle()
                    + ".  Reason: " + reason);
            int systemAlertLayer = mService.mPolicy.windowTypeToLayerLw(
                    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            aboveSystem = windowState.mBaseLayer > systemAlertLayer;
        } else if (appWindowToken != null) {
            Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
                    + "sending to application " + appWindowToken.stringName
                    + ".  Reason: " + reason);
        } else {
            Slog.i(WindowManagerService.TAG, "Input event dispatching timed out "
                    + ".  Reason: " + reason);
        }
        mService.saveANRStateLocked(appWindowToken, windowState, reason);
    }

    if (appWindowToken != null && appWindowToken.appToken != null) {
        // A keyDispatching Timeout 設置
        boolean abort = appWindowToken.appToken.keyDispatchingTimedOut(reason);
        if (! abort) {
            return appWindowToken.inputDispatchingTimeoutNanos;
        }
    } else if (windowState != null) {
        // B inputDispatching time out 設置
        long timeout = ActivityManagerNative.getDefault().inputDispatchingTimedOut(
                windowState.mSession.mPid, aboveSystem, reason);
        if (timeout >= 0) {
            return timeout * 1000000L; //轉化為納秒
        }
    }
    return 0;
}

A.keyDispatching Timeout 設置

  1. ActivityRecord.Token.keyDispatchingTimedOut
@Override
    public boolean keyDispatchingTimedOut(String reason, int windowPid) {
        ActivityRecord anrActivity;
        ProcessRecord anrApp;
        boolean windowFromSameProcessAsActivity;
        synchronized (service) {
            anrActivity = getWaitingHistoryRecordLocked();
            anrApp = app;
            windowFromSameProcessAsActivity =
                    app == null || app.pid == windowPid || windowPid == -1;
        }
        if (windowFromSameProcessAsActivity) {
            return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
        } else {
            // In this case another process added windows using this activity token. So, we call the
            // generic service input dispatch timed out method so that the right process is blamed.
            return service.inputDispatchingTimedOut(windowPid, false /* aboveSystem */, reason) < 0;
        }
    }

調用AMS.inputDispatchingTimedOut

B.inputDispatching time out 設置
1.調用的是AMS.inputDispatchingTimedOut

ublic long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
    ...
    ProcessRecord proc;
    long timeout;
    synchronized (this) {
        synchronized (mPidsSelfLocked) {
            proc = mPidsSelfLocked.get(pid); //根據(jù)pid查看進程record
        }
        timeout = getInputDispatchingTimeoutLocked(proc);
    }

    if (!inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
        return -1;
    }

    return timeout;
}

設置時間

// How long we wait until we timeout on key dispatching.
    static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
        if (r != null && (r.instr != null || r.usingWrapper)) {
            return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
        }
        return KEY_DISPATCHING_TIMEOUT;
    }

2.調用的是AMS.inputDispatchingTimedOut

public boolean inputDispatchingTimedOut(final ProcessRecord proc,
        final ActivityRecord activity, final ActivityRecord parent,
        final boolean aboveSystem, String reason) {
    ...
    final String annotation;
    if (reason == null) {
        annotation = "Input dispatching timed out";
    } else {
        annotation = "Input dispatching timed out (" + reason + ")";
    }

    if (proc != null) {
        ...
        mHandler.post(new Runnable() {
            public void run() {
                appNotResponding(proc, activity, parent, aboveSystem, annotation);
            }
        });
    }
    return true;
}

4.ANR工作

調用后都觸發(fā)了appNotResponding 下面來介紹下這個

ActivityManagerService appNotResponding

final void appNotResponding(ProcessRecord app, ActivityRecord activity,
        ActivityRecord parent, boolean aboveSystem, final String annotation) {
    ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
    SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);

    if (mController != null) {
        try {
            // 0 == continue, -1 = kill process immediately
            int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation);
            if (res < 0 && app.pid != MY_PID) {
                app.kill("anr", true);
            }
        } catch (RemoteException e) {
            mController = null;
            Watchdog.getInstance().setActivityController(null);
        }
    }

    long anrTime = SystemClock.uptimeMillis();
    if (MONITOR_CPU_USAGE) {
        updateCpuStatsNow();
    }

    synchronized (this) {
        // PowerManager.reboot() 會阻塞很長時間蓖柔,因此忽略關機時的ANR
        if (mShuttingDown) {
            Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
            return;
        } else if (app.notResponding) {
            Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
            return;
        } else if (app.crashing) {
            Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
            return;
        }

        app.notResponding = true;

        //記錄ANR
        EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
                app.processName, app.info.flags, annotation);

        // Dump thread traces as quickly as we can, starting with "interesting" processes.
        firstPids.add(app.pid);

        int parentPid = app.pid;
        if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid;
        if (parentPid != app.pid) firstPids.add(parentPid);

        if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);

        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
            ProcessRecord r = mLruProcesses.get(i);
            if (r != null && r.thread != null) {
                int pid = r.pid;
                if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
                    if (r.persistent) {
                        firstPids.add(pid);
                    } else {
                        lastPids.put(pid, Boolean.TRUE);
                    }
                }
            }
        }
    }

    //輸出ANR到main log.
    StringBuilder info = new StringBuilder();
    info.setLength(0);
    info.append("ANR in ").append(app.processName);
    if (activity != null && activity.shortComponentName != null) {
        info.append(" (").append(activity.shortComponentName).append(")");
    }
    info.append("\n");
    info.append("PID: ").append(app.pid).append("\n");
    if (annotation != null) {
        info.append("Reason: ").append(annotation).append("\n");
    }
    if (parent != null && parent != activity) {
        info.append("Parent: ").append(parent.shortComponentName).append("\n");
    }

    final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
    //dump棧信息
    File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
            NATIVE_STACKS_OF_INTEREST);

    String cpuInfo = null;
    if (MONITOR_CPU_USAGE) {
        updateCpuStatsNow();
        synchronized (mProcessCpuTracker) {
            //輸出各個進程的CPU使用情況
            cpuInfo = mProcessCpuTracker.printCurrentState(anrTime);
        }
        //輸出CPU負載
        info.append(processCpuTracker.printCurrentLoad());
        info.append(cpuInfo);
    }

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

    Slog.e(TAG, info.toString());
    if (tracesFile == null) {
        //發(fā)送signal 3來dump棧信息
        Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
    }
    //將anr信息添加到dropbox
    addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
            cpuInfo, tracesFile, null);

    if (mController != null) {
        try {
            // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
            int res = mController.appNotResponding(app.processName, app.pid, info.toString());
            if (res != 0) {
                if (res < 0 && app.pid != MY_PID) {
                    app.kill("anr", true);
                } else {
                    synchronized (this) {
                        mServices.scheduleServiceTimeoutLocked(app);
                    }
                }
                return;
            }
        } catch (RemoteException e) {
            mController = null;
            Watchdog.getInstance().setActivityController(null);
        }
    }

    boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
            Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;

    synchronized (this) {
        mBatteryStatsService.noteProcessAnr(app.processName, app.uid);

        if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
            app.kill("bg anr", true);
            return;
        }

        // Set the app's notResponding state, and look up the errorReportReceiver
        makeAppNotRespondingLocked(app,
                activity != null ? activity.shortComponentName : null,
                annotation != null ? "ANR " + annotation : "ANR",
                info.toString());

        //彈出ANR對話框
        Message msg = Message.obtain();
        HashMap<String, Object> map = new HashMap<String, Object>();
        msg.what = SHOW_NOT_RESPONDING_MSG;
        msg.obj = map;
        msg.arg1 = aboveSystem ? 1 : 0;
        map.put("app", app);
        if (activity != null) {
            map.put("activity", activity);
        }

        mUiHandler.sendMessage(msg);
    }
}

5.優(yōu)化方法

1辰企、運行在主線程里的任何方法都盡可能少做事情。特別是况鸣,Activity應該在它的關鍵生命周期方法(如onCreate()和onResume())里盡可能少的去做創(chuàng)建操作牢贸。(可以采用重新開啟子線程的方式,然后使用Handler+Message的方式做一些操作镐捧,比如更新主線程中的ui等)

2潜索、應用程序應該避免在BroadcastReceiver里做耗時的操作或計算臭增。但也不要在子線程里做這些任務(因為 BroadcastReceiver的生命周期短),如果響應Intent廣播需要執(zhí)行一個耗時的動作的話,應用程序應該啟動一個 Service竹习。

3誊抛、避免在Intent Receiver里啟動一個Activity,因為它會創(chuàng)建一個新的畫面整陌,并從當前用戶正在運行的程序上搶奪焦點拗窃。如果你的應用程序在響應Intent廣播時需要向用戶展示什么,你應該使用Notification Manager來實現(xiàn)泌辫。

4.anr異常也是在程序中自己經常遇到的問題随夸,主要的解決辦法自己最常用的就是不要在主線程中做耗時的操作,而應放在子線程中來實現(xiàn)震放,比如采用Handler+mesage的方式宾毒,或者是有時候需要做一些和網(wǎng)絡相互交互的耗時操作就采用asyntask異步任務的方式(它的底層其實Handler+mesage有所區(qū)別的是它是線程池)等,在主線程中更新UI殿遂。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末诈铛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子墨礁,更是在濱河造成了極大的恐慌幢竹,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恩静,死亡現(xiàn)場離奇詭異妨退,居然都是意外死亡,警方通過查閱死者的電腦和手機蜕企,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冠句,“玉大人轻掩,你說我怎么就攤上這事∨车祝” “怎么了唇牧?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長聚唐。 經常有香客問我丐重,道長,這世上最難降的妖魔是什么杆查? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任扮惦,我火速辦了婚禮,結果婚禮上亲桦,老公的妹妹穿的比我還像新娘崖蜜。我一直安慰自己浊仆,他們只是感情好,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布豫领。 她就那樣靜靜地躺著抡柿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪等恐。 梳的紋絲不亂的頭發(fā)上洲劣,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機與錄音课蔬,去河邊找鬼囱稽。 笑死,一個胖子當著我的面吹牛购笆,可吹牛的內容都是我干的粗悯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼同欠,長吁一口氣:“原來是場噩夢啊……” “哼样傍!你這毒婦竟也來了?” 一聲冷哼從身側響起铺遂,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衫哥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后襟锐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體撤逢,經...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年粮坞,在試婚紗的時候發(fā)現(xiàn)自己被綠了蚊荣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡莫杈,死狀恐怖互例,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情筝闹,我是刑警寧澤媳叨,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站关顷,受9級特大地震影響糊秆,放射性物質發(fā)生泄漏。R本人自食惡果不足惜议双,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一痘番、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧聋伦,春花似錦夫偶、人聲如沸界睁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翻斟。三九已至,卻和暖如春说铃,著一層夾襖步出監(jiān)牢的瞬間访惜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工腻扇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留债热,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓幼苛,卻偏偏與公主長得像窒篱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舶沿,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容