0%

综合技术(一):使用 CrashHandler 来获取应用的 crash 信息

1. Android 系统处理 crash 问题的方法

  • Thread 类的方法 setDefaultUncaughtExceptionHandler()(新版本已不支持):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    /**
    * Set the default handler invoked when a thread abruptly terminates
    * due to an uncaught exception, and no other handler has been defined
    * for that thread.
    *
    * <p>Uncaught exception handling is controlled first by the thread, then
    * by the thread's {@link ThreadGroup} object and finally by the default
    * uncaught exception handler. If the thread does not have an explicit
    * uncaught exception handler set, and the thread's thread group
    * (including parent thread groups) does not specialize its
    * <tt>uncaughtException</tt> method, then the default handler's
    * <tt>uncaughtException</tt> method will be invoked.
    * <p>By setting the default uncaught exception handler, an application
    * can change the way in which uncaught exceptions are handled (such as
    * logging to a specific device, or file) for those threads that would
    * already accept whatever &quot;default&quot; behavior the system
    * provided.
    *
    * <p>Note that the default uncaught exception handler should not usually
    * defer to the thread's <tt>ThreadGroup</tt> object, as that could cause
    * infinite recursion.
    *
    * @param eh the object to use as the default uncaught exception handler.
    * If <tt>null</tt> then there is no default handler.
    *
    * @throws SecurityException if a security manager is present and it
    * denies <tt>{@link RuntimePermission}
    * (&quot;setDefaultUncaughtExceptionHandler&quot;)</tt>
    *
    * @see #setUncaughtExceptionHandler
    * @see #getUncaughtExceptionHandler
    * @see ThreadGroup#uncaughtException
    * @since 1.5
    */
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
    // Android-removed: SecurityManager stubbed out on Android
    /*
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
    sm.checkPermission(
    new RuntimePermission("setDefaultUncaughtExceptionHandler");
    )
    }
    */

    defaultUncaughtExceptionHandler = eh;
    }
    • 当 crash 发生的时候,系统就会回调 UncaughtExceptionHandleruncaughtException() 方法,在该方法中就可以捕获并处理异常信息
  • Demo: 典型的异常处理器的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    public class CrashHandler implements UncaughtExceptionHandler {
    private static final String TAG = "CrashHandler";
    private static final boolean DEBUG = true;

    private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/";
    private static final String FILE_NAME = "crash";
    private static final String FILE_NAME_SUFFIX = ".trace";

    private static CrashHandler sInstance = new CrashHandler();
    private UncaughtExceptionHandler mDefaultCrashHandler;
    private Context mContext;

    private CrashHandler() {
    }

    public static CrashHandler getInstance() {
    return sInstance;
    }

    public void init(Context context) {
    mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
    Thread.setDefaultUncaughtExceptionHandler(this);
    mContext = context.getApplication();
    }

    /**
    * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用 #uncaughtException() 方法
    * thread 为出现未捕获异常的线程,ex 为未捕获的异常,有了这个 ex,我们就可以得到异常信息
    */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
    try {
    // 导出异常信息到 SD 卡中
    dumpExceptionToSDCard(ex);
    // 这里可以上传异常信息到服务器,便于开发人员分析日志从而解决 bug
    uploadExceptionToServer();
    } catch (IOException e) {
    e.printStackTrace();
    }

    ex.printStackTrace();
    // 如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
    if (mDefaultCrashHandler != null) {
    mDefaultCrashHandler.uncaughtException(thread, ex);
    } else {
    Process.killProcess(Process.myPid());
    }
    }

    private void dumpExceptionTOSDCard(Throwable ex) throws IOException {
    // 如果 SD 卡不存在或无法使用,则无法把异常信息写入 SD 卡
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    if (DEBUG) {
    Log.w(TAG, "sdcard unmounted, skip dump exception");
    return;
    }
    }

    File dir = new File(PATH);
    if (!dir.exists()) {
    dir.mkdirs();
    }
    long current = System.currentTimeMillis();
    String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
    File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);

    try {
    PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
    pw.println(time);
    dumpPhoneInfo(pw);
    pw.println();
    ex.printStackTrace(pw);
    pw.close();
    } catch (Exception e) {
    Log.e(TAG, "dump carsh info failed");
    }
    }

    private void dumPhoneInfo(PrintWriter pw) throws NameNotFoundException {
    PackageManagerpm = mContext.getPackageManager();
    PackageInfo pi = pm.getPackageIngo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
    pw.print("App Version: ");
    pw.print(pi.versionName);
    pw.print('_');
    pw.println(pi.versionCode);

    // Android 版本号
    pw.print("OS Version: ");
    pw.print(Build.VERSION.RELEASE);
    pw.print("_");
    pw.println(Build.VERSION.SDK_INT);

    // 手机制造商
    pw.print("Vendor: ");
    pw.println(Build.MANUFACTURER);

    // 手机型号
    pw.print("Model: ");
    pw.println(Build.MODEL);

    // CPU 架构
    pw.print("CPU ABI: ");
    pw.println(Build.CPU_ABI);
    }

    private void uploadExceptionToServer() {
    // TODO Upload Exception Message To Your Web Server
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 使用 CrashHandler
    public class TestApp extends Application {
    private static TestApp sInstance;

    @Override
    public void onCreate() {
    super.onCreate();
    sInstance = this;
    // 在这里为应用设置异常处理,然后程序才能获取未处理的异常
    CrashHandler crashHanlder = CrashHandler.getInstance();
    crashHandler.init(this);
    }

    public static TestApp getInstance() {
    return sInstance;
    }
    }
-------------------- 本文结束感谢您的阅读 --------------------