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

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

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

   

java.lang
接口 Thread.UncaughtExceptionHandler
所有已知实现类:
ThreadGroup
正在封闭类:
Thread
public 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)
方法摘要
 void	uncaughtException(Thread t, Throwable e) 
          当给定线程因给定的未捕获异常而终止时,调用该方法。
 
方法详细信息
uncaughtException
void 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();
            }
        }
    }
五、在 u ncaughtException()方法中,来处理我们为捕获异常。如果异常处理类不为空,而且我们自定义的也没有处理(也就是在自定义方法中的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,而是提示出来了我们自定义的消息,是不是体验更好了。效果如图。欢迎评论,互相学习。

android之自定义UncaughtExceptionHandler类来处理未捕获异常_第1张图片




   

你可能感兴趣的:(java,android,自定义异常捕获,捕获异常处理)