android之自定义UncaughtExceptionHandler类来处理未捕获异常

来源:互联网 发布:unity3d场景下载 编辑:程序博客网 时间:2024/05/17 22:01

        我们在开发中,经常会遇到各种各样的异常,造成我们的程序崩溃,系统原生的异常处理粗暴的退出,用户体验很差,那么我们能不能自己来处理为捕获异常呢,以供我们来达到更好的用户体验,以及保存异常,甚至是发送给我们的邮箱。下面就介绍UncaughtExceptionHandler类的使用。

  一、我们先来看API中对UncaughtExceptionHandler类的描述。

   

java.lang接口 Thread.UncaughtExceptionHandler所有已知实现类:ThreadGroup正在封闭类:Threadpublic static interface Thread.UncaughtExceptionHandler当 Thread 因未捕获的异常而突然终止时,调用处理程序的接口。当某一线程因未捕获的异常而即将终止时,Java 虚拟机将使用 Thread.getUncaughtExceptionHandler() 查询该线程以获得其 UncaughtExceptionHandler 的线程,并调用处理程序的 uncaughtException 方法,将线程和异常作为参数传递。如果某一线程没有明确设置其 UncaughtExceptionHandler,则将它的 ThreadGroup 对象作为其 UncaughtExceptionHandler。如果 ThreadGroup 对象对处理异常没有什么特殊要求,那么它可以将调用转发给默认的未捕获异常处理程序。从以下版本开始:1.5另请参见:Thread.setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler), Thread.setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler), ThreadGroup.uncaughtException(java.lang.Thread, java.lang.Throwable)方法摘要 voiduncaughtException(Thread t, Throwable e)           当给定线程因给定的未捕获异常而终止时,调用该方法。 方法详细信息uncaughtExceptionvoid uncaughtException(Thread t,                       Throwable e)当给定线程因给定的未捕获异常而终止时,调用该方法。Java 虚拟机将忽略该方法抛出的任何异常。参数:t - 线程e - 异常
二、从API描述中可以看出UncaughtExceptionHandler类是当线程因为捕获异常而突然中止时,调用此接口。并实现uncaughtException()方法处理此异常。这样我们就可以得知,当我们的程序出现为捕获异常时候,我们可以实现此接口,重写中的uncaughtException()方法来处理我们的异常,实现我们自己的处理异常功能。

   

public class MyExceptionHandler  implements Thread.UncaughtExceptionHandler{    @Override    public void uncaughtException(Thread thread, Throwable throwable) {            }}
三、实现我们自定义异常处理类的构造函数,初始化系统默认的异常处理类,并设置为当前线程处理。下面代码中的MyApplication是我自定义的application类,以便在我们自己的应用中实现我们的异常处理类,稍后会讲解。

/** * 项目名称:MeiJianFang * 类描述: * 创建人:cdy * 创建时间:2016/3/22 * 修改人:cdy * 修改时间:16:37 * 修改备注: */public class MyExceptionHandler  implements Thread.UncaughtExceptionHandler{    /** 声明系统默认的UncaughtException处理类 */    private Thread.UncaughtExceptionHandler mDefaultHandler;    //声明我们自定义的application    MyApplication application;    public MyExceptionHandler(MyApplication application) {        //初始化系统的异常处理类。        this.mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();        //设置此异常处理类为当前的线程处理。        Thread.setDefaultUncaughtExceptionHandler(this);        this.application = application;    }    //实现异常处理的方法    @Override    public void uncaughtException(Thread thread, Throwable throwable) {    }}
四、我们自定义一个处理异常的方法来处理传进来的异常,传进来异常信息类Throwable ex。如果ex不为空则表示系统未处理,我们执行下面我们自行的处理信息方法,提示客户一句话,“应用发生异常,程序即将退出”,返回false。如果为空,择表示系统处理了,返回true。代码中顺便把保存错误日志信息的方法也粘出来,供大家参考。

  /**     * @brief 自定义错误处理,收集错误信息     * @details 发送错误报告等操作均在此完成     * @param ex 异常信息     * @return true:如果处理了该异常信息;否则返回false。     */    private boolean handleException(final Throwable ex) {        //拿到程序的异常,如果是属于程序可以自行处理的,就返回true,如果是程序未处理的,就执行我们的操作。        //当然了我们的关键点是程序未处理的异常。当程序未处理的时候,提示下面的错误信息。        if (ex == null) {            return true;        }        ex.printStackTrace();        // 提示错误消息        new Thread() {            @Override            public void run() {                Looper.prepare();                Toast.makeText(application.getApplicationContext(), "应用发生异常,即将退出!", Toast.LENGTH_LONG).show();                Looper.loop();            }        }.start();        // 保存错误报告文件        saveCrashInfoToFile(ex);        return true;    }    /**     * @brief 保存错误信息到文件中     * @param ex 异常     */    private void saveCrashInfoToFile(Throwable ex) {        //返回一个代表该线程的堆栈转储堆栈跟踪元素的数组,有哪位可以通俗点的讲解一下这句话,欢迎评论。        //下面的for循环可以拿到遍历,来让异常消息换行。        final StackTraceElement[] stack = ex.getStackTrace();        final String message = ex.getMessage();        /* 准备错误日志文件 */        //FileUtil.APP_LOG_PATH 是获取的当前sd卡根目录,创建我们将要存储日志的文件。        File logFile = new File(FileUtil.APP_LOG_PATH + LOG_NAME);        if (!logFile.getParentFile().exists()) {            logFile.getParentFile().mkdirs();        }        /* 写入错误日志 */        FileWriter fw = null;        final String lineFeed = "\r\n";        try {            fw = new FileWriter(logFile, true);            //StringUtil.currentTime(StringUtil.FORMAT_YMDHMS).toString() 我自定义的获取系统当前日期的方法。            fw.write(StringUtil.currentTime(StringUtil.FORMAT_YMDHMS).toString() + lineFeed                    + lineFeed);            fw.write(message + lineFeed);            for (int i = 0; i < stack.length; i++) {                fw.write(stack[i].toString() + lineFeed);            }            fw.write(lineFeed);            fw.flush();        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (null != fw)                    fw.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }
五、在uncaughtException()方法中,来处理我们为捕获异常。如果异常处理类不为空,而且我们自定义的也没有处理(也就是在自定义方法中的return true),那么就交由系统自行处理。如果我们处理了,那么线程休息3秒钟,杀死进程。休息三秒钟以供我们可以有时间弹出我们自定义的异常处理消息。

  

//实现异常处理的方法    @Override    public void uncaughtException(Thread thread, Throwable ex) {        // 首先使用我们自定义的异常处理类来处理,如果是未捕获异常,那么我们自行处理,如果不是那么不处理,返回true,没有处理则让系统默认的异常处理器来处理,        if (!handleException(ex) && mDefaultHandler != null) {            mDefaultHandler.uncaughtException(thread, ex);        } else {            // 等待会后结束程序            try {                Log.i(LOG_NAME, "exit start");                Thread.sleep(3000);                android.os.Process.killProcess(android.os.Process.myPid());                System.exit(10);                application.finishActivity();                Log.i(LOG_NAME,"exit end");            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }

六、自定义的异常处理类完整代码如下。

 

package com.meijianfang.appliction;import android.os.Looper;import android.util.Log;import android.widget.Toast;import com.meijianfang.tool.FileUtil;import com.meijianfang.tool.StringUtil;import java.io.File;import java.io.FileWriter;import java.io.IOException;/** * 项目名称:MeiJianFang * 类描述: * 创建人:cdy * 创建时间:2016/3/22 * 修改人:cdy * 修改时间:16:37 * 修改备注: */public class MyExceptionHandler  implements Thread.UncaughtExceptionHandler{    /** 声明系统默认的UncaughtException处理类 */    private Thread.UncaughtExceptionHandler mDefaultHandler;    //声明我们自定义的application    MyApplication application;    /** 错误日志文件名称 */    static final String LOG_NAME = "/crash.txt";    public MyExceptionHandler(MyApplication application) {        //初始化系统的异常处理类。        this.mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();        //设置此异常处理类为当前的线程处理。        Thread.setDefaultUncaughtExceptionHandler(this);        this.application = application;    }    //实现异常处理的方法    @Override    public void uncaughtException(Thread thread, Throwable ex) {        // 首先使用我们自定义的异常处理类来处理,如果是未捕获异常,那么我们自行处理,如果不是那么不处理,返回true,没有处理则让系统默认的异常处理器来处理,        if (!handleException(ex) && mDefaultHandler != null) {            mDefaultHandler.uncaughtException(thread, ex);        } else {            // 等待会后结束程序            try {                Log.i(LOG_NAME, "exit start");                Thread.sleep(3000);                android.os.Process.killProcess(android.os.Process.myPid());                System.exit(10);                application.finishActivity();                Log.i(LOG_NAME,"exit end");            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    /**     * @brief 自定义错误处理,收集错误信息     * @details 发送错误报告等操作均在此完成     * @param ex 异常信息     * @return true:如果处理了该异常信息;否则返回false。     */    private boolean handleException(final Throwable ex) {        //拿到程序的异常,如果是属于程序可以自行处理的,就返回true,如果是程序未处理的,就执行我们的操作。        //当然了我们的关键点是程序未处理的异常。当程序未处理的时候,提示下面的错误信息。        if (ex == null) {            return true;        }        ex.printStackTrace();        // 提示错误消息        new Thread() {            @Override            public void run() {                Looper.prepare();                Toast.makeText(application.getApplicationContext(), "应用发生异常,即将退出!", Toast.LENGTH_LONG).show();                Looper.loop();            }        }.start();        // 保存错误报告文件        saveCrashInfoToFile(ex);        return true;    }    /**     * @brief 保存错误信息到文件中     * @param ex 异常     */    private void saveCrashInfoToFile(Throwable ex) {        //返回一个代表该线程的堆栈转储堆栈跟踪元素的数组,有哪位可以通俗点的讲解一下这句话,欢迎评论。        //下面的for循环可以拿到遍历,来让异常消息换行。        final StackTraceElement[] stack = ex.getStackTrace();        final String message = ex.getMessage();        /* 准备错误日志文件 */        //FileUtil.APP_LOG_PATH 是获取的当前sd卡根目录,创建我们将要存储日志的文件。        File logFile = new File(FileUtil.APP_LOG_PATH + LOG_NAME);        if (!logFile.getParentFile().exists()) {            logFile.getParentFile().mkdirs();        }        /* 写入错误日志 */        FileWriter fw = null;        final String lineFeed = "\r\n";        try {            fw = new FileWriter(logFile, true);            //StringUtil.currentTime(StringUtil.FORMAT_YMDHMS).toString() 我自定义的获取系统当前日期的方法。            fw.write(StringUtil.currentTime(StringUtil.FORMAT_YMDHMS).toString() + lineFeed                    + lineFeed);            fw.write(message + lineFeed);            for (int i = 0; i < stack.length; i++) {                fw.write(stack[i].toString() + lineFeed);            }            fw.write(lineFeed);            fw.flush();        } catch (IOException e) {            e.printStackTrace();        } finally {            try {                if (null != fw)                    fw.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}
七、如何在程序中使用呢,这下就用到了我们自定义的Appliction类了。首先实现系统的Appliction接口,重写onCreate()方法,在方法中初始化我们刚刚自定义的异常捕获类。

 

package com.meijianfang.appliction;import android.app.Application;/** * 项目名称:MeiJianFang * 类描述: * 创建人:cdy * 创建时间:2016/3/22 * 修改人:cdy * 修改时间:17:01 * 修改备注: */public class MyApplictionHandler extends Application {    @Override    public void onCreate() {        super.onCreate();        /* 全局异常崩溃处理 */        MyExceptionHandler catchExcep = new MyExceptionHandler(this);        Thread.setDefaultUncaughtExceptionHandler(catchExcep);    }}
八、在我们应用的AndroidManifest.xml文件中引用我们自己定义的Application类,作为应用的application类来使用。

 <application        android:name=".appliction.MyApplictionHandler"        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:supportsRtl="true"        android:theme="@style/AppTheme">
最后我们运行我们的程序,我这里在首页写了一个空指针的异常。系统并没有直接close,而是提示出来了我们自定义的消息,是不是体验更好了。效果如图。欢迎评论,互相学习。






   

0 0
原创粉丝点击