您的位置:首页 > 其它

Crash原因捕捉利器

2016-01-28 17:17 519 查看

Thread.UncaughtExceptionHandler接口简介

public static interface Thread.UncaughtExceptionHandler

当 Thread 因未捕获的异常而突然终止时,调用处理程序的接口。

当某一线程因未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询该线程以获得其 UncaughtExceptionHandler 的线程,并调用处理程序的 uncaughtException 方法,将线程和异常作为参数传递。如果某一线程没有明确设置其 UncaughtExceptionHandler,则将它的 ThreadGroup 对象作为其 UncaughtExceptionHandler。如果 ThreadGroup 对象对处理异常没有什么特殊要求,那么它可以将调用转发给默认的未捕获异常处理程序。

方法详细信息

[code]    /**
     * 当给定线程因给定的未捕获异常而终止时,调用该方法。
     * Java 虚拟机将忽略该方法抛出的任何异常。
     * @param thread 线程
     * @param ex 异常
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {

    }


上述是API给UncaughtExceptionHandler的解释,目前很多项目中都会使用这个接口来监听程序的异常情况、例如,我们开发了一个程序由于考虑不周全等原因程序在上线之后收到了用户的反馈,收程序崩溃了。对于程序员来说这是一个不幸的消息,什么bug?崩溃日志是什么?。。。这些问题用户可帮不了你!没关系有了UncaughtExceptionHandler这个接口之后这些问题就不难解决了。我们在来写一个小demo演示一下如何解决类似的问题,废话不多说,直接上代码。

MainActivity.java

[code]import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity implements View.OnClickListener {

    Button btnTest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnTest = (Button) findViewById(R.id.btn_test);
        btnTest.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_test:                       
                // 制造一个异常
                int a = 10 / 0;
                break;
        }
    }
}


上述是测试代码,activity_main.xml中只有一个id为btn_test的Button,此处就不帖代码了。接下来看一下Application中得代码。

[code]import com.lyong.baselibrary.base.BaseApplication;
import com.lyong.baselibrary.utils.CrashHandler;

public class App extends BaseApplication {

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


App.java中的代码很简单,就是在onCreate()方法中初始化CrashHandler,这样就保证从应用的启动到退出一直监听着异常情况了。

接下来看看我们的核心类CrashHandler.java的代码。

[code]package com.lyong.baselibrary.utils;

import android.content.Context;
import android.os.Looper;
import android.widget.Toast;

import java.lang.Thread.UncaughtExceptionHandler;

/**
 * 异常处理类
 *
 * @author Lyong
 *         Date:2015-08-06
 */
public class CrashHandler implements UncaughtExceptionHandler {

    private Context mContext;
    private static CrashHandler mInstance = new CrashHandler();
    private UncaughtExceptionHandler mDefalutHandler;

    private CrashHandler() {
    }

    /**
     * 获取单例
     *
     * @return CrashHandler单例
     */
    public static CrashHandler getInstance() {
        return mInstance;
    }

    public void init(Context context) {
        // 获取上下文对象
        mContext = context.getApplicationContext();
        // 获取系统默认的UncaughtException处理器
        mDefalutHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当给定线程因给定的未捕获异常而终止时,调用该方法。
     * Java 虚拟机将忽略该方法抛出的任何异常。
     *
     * @param thread 线程
     * @param ex     异常
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        // 是否处理
        boolean hasHandle = handleException(ex);

        // 如果用户没有处理则让系统默认的异常处理器来处理
        if(!hasHandle && mDefalutHandler != null) {
            mDefalutHandler.uncaughtException(thread,ex);
        } else {
            try{
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                Logger.e("error:" + e);
            }

            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }

    }

    private boolean handleException(final Throwable ex) {
        if(ex == null) {
            return  false;
        }

        new Thread() {

            @Override
            public void run() {
                Looper.prepare();

                ex.printStackTrace();

                String err = "[" + ex.getMessage() + "]";
                Toast.makeText(mContext,"程序异常,5秒后自动退出",Toast.LENGTH_SHORT).show();

                Looper.loop();
            }

        }.start();

        // 收集设备参数信息,日志信息
        String str = collectDeviceInfo(ex);
        // 保存日志文件
        saveCrashInfoToFile(str);

        return true;
    }

    /**
     * 保存出错信息
     * @param error 待保存的出错信息
     */
    private void saveCrashInfoToFile(String error) {
        Logger.e("saveCrashInfoToFile:"+error);
        // TODO 保存error到文件或者上传到服务器
    }

    /**
     * 收集设备信息,日志信息
     * @param ex Throwable
     * @return 收集的信息
     */
    private String collectDeviceInfo(Throwable ex) {
        Logger.e("collectDeviceInfo:" + ex.getMessage());
        // TODO 获取设备信息
        return null;
    }
}


现在来说明一下CrashHandler的代码,首先此类是一个单例类,并拥有一个public 的init方法,在App.java中的onCreate()方法中被调用,这个方法是用来初始化CrashHandler的。

[code]public void init(Context context) {
        // 获取上下文对象
        mContext = context.getApplicationContext();
        // 获取系统默认的UncaughtException处理器
        mDefalutHandler =     Thread.getDefaultUncaughtExceptionHandler();
        // 设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }


有详细的代码注释,就不在解释了。接下来我们看看下一个方法。

[code]/**
     * 当给定线程因给定的未捕获异常而终止时,调用该方法。
     * Java 虚拟机将忽略该方法抛出的任何异常。
     *
     * @param thread 线程
     * @param ex     异常
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        // 是否处理
        boolean hasHandle = handleException(ex);

        // 如果用户没有处理则让系统默认的异常处理器来处理
        if(!hasHandle && mDefalutHandler != null) {
            mDefalutHandler.uncaughtException(thread,ex);
        } else {
            try{
                Thread.sleep(5 * 1000);
            } catch (InterruptedException e) {
                Logger.e("error:" + e);
            }

            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }

    }


这是UncaughtExceptionHandler中声明的方法,也是我们重点学习的。当给定线程因给定的未捕获异常而终止时,回调该方法。当程序中有未捕获的异常发生时(注意:是未捕获的异常,如果try-catch过的不会回调此方法)会回调此方法。接下来看看handleException方法:

[code]private boolean handleException(final Throwable ex) {
        if(ex == null) {
            return  false;
        }

        new Thread() {

            @Override
            public void run() {
                Looper.prepare();

                ex.printStackTrace();

                String err = "[" + ex.getMessage() + "]";
                Toast.makeText(mContext,"程序异常,5秒后自动退出",Toast.LENGTH_SHORT).show();

                Looper.loop();
            }

        }.start();

        // 收集设备参数信息,日志信息
        String str = collectDeviceInfo(ex);
        // 保存日志文件
        saveCrashInfoToFile(str);

        return true;
    }


这个方法首先判断,如果异常信息为空则不做任何处理,直接返回false,否则输出异常信息,提示用户。collectDeviceInfo方法和saveCrashInfoToFile方法分别是手机设备信息和保存崩溃日志的方法,这里可以根据具体的业务要求来做不同的动作,这里只是举个例子,具体的实现方式大家可以尝试完善一下。

handleException方法返回一个boolean类型保存在uncaughtException方法中的hasHandle变量中,接下来uncaughtException方法会判断如果用户没有处理则让系统默认的异常处理器来处理,如果用户处理过了则休眠5秒钟,然后Kill掉进程。

demo接讲到这里,通过上述的方法则可以收集一些未知崩溃信息了。从而不断的优化我们的应用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: