1. 程式人生 > >Android Fk:【Stability】應用FC彈窗流程

Android Fk:【Stability】應用FC彈窗流程

Android Fk:【Stability】應用FC彈窗流程

一.概述

在Android手機裡,使用者可見的應用crash有兩種情況:
1. 出錯後彈出出錯提示框
2. 應用直接閃退

二.應用FC退出流程

應用程序FC異常分為Java異常FC,Native異常FC

2.1 Java FC Crash彈窗流程

這裡寫圖片描述
這裡注意到呼叫AMS方法時是同步binder call,因此會在dialog show出來處理完畢後才會走回來kill 自己.

//frameworks/base/services/core/java/com/android/server/am/AppErrors.java
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, int callingPid, int callingUid) { synchronized (mService) { final Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG; task = data.task
; msg.obj = data; mService.mUiHandler.sendMessage(msg); } int res = result.get();//這裡會等待dialog處理,主動wait() ... }
//frameworks/base/services/core/java/com/android/server/am/AppErrorResult.java
final class AppErrorResult {
    public void set(int res) {
        synchronized (this
) { mHasResult = true; mResult = res; notifyAll(); } } public int get() { synchronized (this) { while (!mHasResult) { try { wait(); } catch (InterruptedException e) { } } } return mResult; } boolean mHasResult = false; int mResult; }

2.2 Native FC Crash彈窗流程

這裡寫圖片描述
彈窗退出方式即便程序是在後臺,還是會show出dialog,使用者是可以察覺的.

2.3 應用閃退

看下RuntimeInit.java中的code,在commitInit()中設定了兩個UncaughtExceptionHandler,其中setUncaughtExceptionPreHandler()不是公開的介面,setDefaultUncaughtExceptionHandler()是公開的介面.

//frameworks/base/core/java/com/android/internal/os/RuntimeInit.java
protected static final void commonInit() {
        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");

        /*
         * set handlers; these apply to all threads in the VM. Apps can replace
         * the default handler, but not the pre handler.
         */
        Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
}

這裡設定了Thread預設的UncaughtExceptionHandler為KillApplicationHandler:

    /**
     * Handle application death from an uncaught exception.  The framework
     * catches these for the main threads, so this should only matter for
     * threads created by applications.  Before this method runs,
     * {@link LoggingHandler} will already have logged details.
     */
    private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            try {
                ...
                //這裡跨程序去通知AMS show dialog,同步binder call,會等待dialog處理結束(即dismiss掉)再回來
                ActivityManager.getService().handleApplicationCrash(
                        mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
            } catch (Throwable t2) {
               ...
            } finally {
                // 最後kill掉自己並退出
                Process.killProcess(Process.myPid());
                System.exit(10);
            }
        }
    }

可以看出KillApplicationHandler在kill自己退出之前會去先同步binder呼叫show error dialog;
而setDefaultUncaughtExceptionHandler是公開的介面,應用可以自己重寫UncaughtExceptionHandler,然後呼叫Thread.setDefaultUncaughtExceptionHandler()來設定default為重寫的handler,從而實現異常時自己需要處理的操作,比如抓取特定的log儲存待上傳等.

一般良心應用的操作,在application初始化的時候先獲取儲存預設的UncaughtExceptionHandler,再設定default為重寫的UncaughtExceptionHandler,等異常時先呼叫重寫的UncaughtExceptionHandler,等自己的邏輯走完後再呼叫儲存的原來的UncaughtExceptionHandler走系統預設的處理方式.

比如訊飛語音的操作:

public class d implements UncaughtExceptionHandler {
    private static d c;
    private UncaughtExceptionHandler a;
    private Context b;

    private d(Context context) {
        if (Thread.getDefaultUncaughtExceptionHandler() != this) {
            //將預設的UncaughtExceptionHandler儲存在a中
            this.a = Thread.getDefaultUncaughtExceptionHandler();
            //設定預設的為複寫後的
            Thread.setDefaultUncaughtExceptionHandler(this);
            this.b = context.getApplicationContext();
        }
    }

    private String a(Throwable th) {
        String str = null;
        if (th != null) {
            try {
                Writer stringWriter = new StringWriter();
                PrintWriter printWriter = new PrintWriter(stringWriter);
                th.printStackTrace(printWriter);
                printWriter.close();
                str = stringWriter.toString();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return str;
    }
    //該方法猜測應該是在訊飛應用application中進行例項化
    public static void a(Context context) {
        if (c == null) {
            c = new d(context);
        }
    }

    public void uncaughtException(Thread thread, Throwable th) {
        //先處理自己的邏輯
        if (a.h.booleanValue()) {
            String a = a(th);
            c cVar = new c();
            cVar.a = a.d;
            cVar.b = e.a(a);
            cVar.c = System.currentTimeMillis();
            f.a(cVar);
        }
        new com.iflytek.sunflower.d.d(this.b).a();
        if (this.a != null) {
            //再呼叫系統原來預設的,如果前面沒有kill掉自己,在這裡會走系統原生的killApplicationHandler
            //從而會彈出FC dialog.
            this.a.uncaughtException(thread, th);
        }
    }
}

如果程序在後臺不可見,該退出方式使用者是察覺不到的.
http://blog.csdn.net/TaylorPotter/article/details/79305155

三. 應用程序啟動

應用程序會在啟動時去設定DefaultUncaughtExceptionHandler,而DefaultUncaughtExceptionHandler是可以複寫重新設定的.
簡單圖:
這裡寫圖片描述

四. 總結

  1. Java FC 彈窗詳細流程
    這裡寫圖片描述
    點選檢視大圖
  2. Native FC 彈窗詳細流程
    這裡寫圖片描述
    點選檢視大圖

  3. 應用啟動詳細流程
    這裡寫圖片描述
    點選檢視大圖

  4. 以上流程圖的uml程式碼
    百度雲下載上面圖中的uml程式碼