使用CrashHandler获取应用crash信息

  Android应用不可避免会发生crash,也称之为崩溃。发生原因可能是由于Android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络情况。当crash发生时,系统会kill掉正在执行的程序,现象就是闪退或者提示用户程序已停止运行。更糟糕的是,当用户发生了crash,开发者却无法得知程序为何crash,因此需要知道用户当时的crash信息。为此Android提供了处理这类问题的方法,即Thread类中的一个方法setDefaultUncaughtExceptionHandler

/**
* Sets the default uncaught exception handler. 
* This handler is invoked in case any Thread due to unhandled exception.
*
* @param handler
*           The handler to set or null.
*/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler){
    Thread.defaultUncaughtHandler = handler;
}

  当crash发生的时候,系统就会回调UncaughtExceptionHandler的uncaughtException方法,在uncaughtException方法中就可以获取到异常信息。

  下面是一个典型的异常处理器的实现:

public class CrashHandler implements Thread.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 Thread.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.getApplicationContext();
    }

    /**
     * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaught-
     * 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{
            android.os.Process.killProcess(android.os.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 crash info failing");
        }
    }

    private void dumpPhoneInfo(PrintWriter pw)throws PackageManager.NameNotFoundException{
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(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 Web Server
    }
}

  上面的CrashHandler使用也很简单,可以选择在Application初始化的时候为线程设置CrashHandler,如下所示:

public class MainApplication extends MultiDexApplication {
    private static MainApplication sInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init(this);
    }

    public static MainApplication getInstance(){
        return sInstance;
    }
posted @ 2020-08-19 10:10  Hyeri2333  阅读(456)  评论(0编辑  收藏  举报