Android 全局异常捕获的完整实践

转:http://blog.qiji.tech/archives/9610?utm_source=tuicool&utm_medium=referral

在 Android 开发中在所难免的会出现程序 Crash,俗称崩溃。用户的随意性访问出现测试时未知的 Bug 导致我们的程序 Crash,此时我们是无法直接获取的错误 Log 的。Crash 极大的影响用户体验,甚至很可能因此被卸载。为了获取错误的 Log 信息,就强烈需要捕获全局的异常信息,当程序出现Crash 信息,记录 Log,上传到服务器,以便开发分析、修复 Bug。


原理

当程序中出现异常,会自动检测该异常是否被 try catch 捕获,如果没有捕获,则向上传递,直到被系统捕获、处理。系统默认有一个实现了 Thread.UncaughtExceptionHandler 接口的对象,并在初始化注册。专门用来捕获异常。我们要做的就是实现自己的 UncaughtExceptionHandler ,并通过 set 方法,替换掉系统默认的。

实现

定义一个类,实现 Thread.UncaughtExceptionHandler,提供初始化方法,并在 Application 的 onCreate 初始化。由于异常捕获全局只需要一个对象,所以最好用单例。

public class CrashHandler implements 
                Thread.UncaughtExceptionHandler{
 
    private static Object lock = new Object();
 
    private CrashHandler(){
        // Empty Constractor
    }
 
    private static CrashHandler mCrashHandler;
    private Context mContext;
 
    public static CrashHandler getInstance(){
        synchronized (lock) {
            if (mCrashHandler == null) {
                synchronized (lock) {
                    if (mCrashHandler == null) {
                        mCrashHandler = new CrashHandler();
                    }
                }
            }
 
            return mCrashHandler;
        }
    }
 
    /* 初始化 */
    public void init(Context context){
        this.mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
 
    @Override
    public void uncaughtException(Thread thread, final Throwable ex) {
        new Thread(new Runnable() {
 
            @Override
            public void run() {
                Looper.prepare();
 
                ToastUtils.show("程序发生了点小意外,即将关闭... "+
                        ex.getMessage());
                Looper.loop();
 
                SystemClock.sleep(3000);
 
                // 将Activity的栈清空
                AppManager.removeAll();
                // 退出程序
                Process.killProcess(Process.myPid());
                // 关闭虚拟机,彻底释放内存空间
                System.exit(0);
 
            }
        }).start();
    }
}

注意: 学 JavaSE 时,知道 Java 的异常处理是另开线程的,所以不能在子线程直接更新UI,需要加上 Loop。

BaseApplication.java

public class BaseApplication extends Application{
 
    @Override
    public void onCreate() {
        super.onCreate();
 
        // ...
 
        // 注册全局异常处理
        CrashHandler.getInstance().init(this);
    }
}

优化

以上是基本的实现,既然异常捕获了,就要将异常和设备信息上传到服务器。排除没有必要捕获的异常。

public class CrashHandler implements 
                Thread.UncaughtExceptionHandler{
 
    private static Object lock = new Object();
 
    private CrashHandler(){
        // Empty Constractor
    }
 
    private static CrashHandler mCrashHandler;
    private Context mContext;
    private UncaughtExceptionHandler defaultUncaughtExceptionHandler;
 
    public static CrashHandler getInstance(){
        synchronized (lock) {
            if (mCrashHandler == null) {
                synchronized (lock) {
                    if (mCrashHandler == null) {
                        mCrashHandler = new CrashHandler();
                    }
                }
            }
 
            return mCrashHandler;
        }
    }
 
    /* 初始化 */
    public void init(Context context){
        this.mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(this);
        defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
    }
 
    @Override
    public void uncaughtException(Thread thread, final Throwable ex) {
        if (isHandler(ex)){
            handlerException(ex);
        } else {
            defaultUncaughtExceptionHandler.uncaughtException(thread, ex);
        }
    }
 
    private boolean isHandler(Throwable ex){
        // 排序不需要捕获的情况
        if (ex == null){
            return false;
        }
 
        // ...
 
        return true;
 
    }
 
    private void handlerException(final Throwable ex) {
        new Thread(new Runnable() {
 
            @Override
            public void run() {
                Looper.prepare();
 
                ToastUtils.show("程序发生了点小意外,即将关闭... "+
                        ex.getMessage());
                Looper.loop();
 
                // 上传至服务器
                connectionServer();
 
                // 将Activity的栈清空
                AppManager.removeAll();
                // 退出程序
                Process.killProcess(Process.myPid());
                // 关闭虚拟机,彻底释放内存空间
                System.exit(0);
 
            }
 
        }).start();
    }
}

设备信息

可在 android.os.Build 类找

Build.BOARD // 主板
Build.BRAND // android系统定制商
Build.CPU_ABI // cpu指令集
Build.DEVICE // 设备参数
Build.DISPLAY // 显示屏参数
Build.FINGERPRINT // 硬件名称
Build.HOST
Build.ID // 修订版本列表
Build.MANUFACTURER // 硬件制造商
Build.MODEL // 版本
Build.PRODUCT // 手机制造商
Build.TAGS // 描述build的标签
Build.TIME
Build.TYPE // builder类型
Build.USER



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值