Android APP全局捕获异常并保存至本地

    开篇就借用一下清代黄宗羲的《明儒学案》中的一句话与各位共勉——“学则智,不学则愚”。我们一起加油ヾ(◍°∇°◍)ノ゙!!

    在做项目的时候,当程序出现异常时,如果能够及时捕获到并上传到服务器,那么这样我们就能够看到异常日志信息了。本文就来实现这个功能。

    如果某个应用安装的Thread.UncaughtExceptionHandler未移交给默认的Thread.UncaughtExceptionHandler,则当出现未捕获的异常时,系统不会终止应用,即不会出现系统默认的“抱歉,xxx应用已停止运行”。因此,我们就可以对应用的异常进行全局捕获,从而将异常保存下来。首先,新建CrashHandler类并实现Thread.UncaughtExceptionHandler接口,并重写public void uncaughtException(Thread thread, Throwable ex)方法,当产生异常时就会转入该函数处理。

 /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if (!handleException(ex) && mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {

            }
        }
        //退出程序
        ExitAppUtils.getInstance().exit();
    }

    最后一行代码用来退出程序,之前在网上查找资料,退出程序的方式多为:android.os.Process.killProcess(android.os.Process.myPid())、System.exit(0)或者System.exit(2),但在实际操作中,程序并不会完美退出,而是会多次执行handleException()方法。本文的ExitAppUtils()类使用List来对APP的Activity进行管理,现将所有的Activity销毁,最后实现程序的退出,该类的代码将在本文末尾给出。

    正常情况下,uncaughtException()方法会调用handleException()方法对异常进行处理,之后睡两秒程序退出。我们也可以在handleException()方法中进行设置,从而给出某种特殊情况交由系统默认的异常处理器来处理。

    我们来看一下handleException()方法是怎样对异常进行处理的:

/**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                CustomToast.showLongError(mContext, "很抱歉,程序出现异常,即将退出");
                Looper.loop();
            }
        }.start();
        collectDeviceInfo(ex);//收集设备参数等信息
        saveCrashInfo2Json();//保存日志文件
        return true;
    }

     新开的线程用来对用户进行提示,时长为之前sleep的长度。关于收集设备参数信息,上一篇博客Android 获取手机及APP信息实例详解已经进行了说明,本文不再赘述。saveCrashInfo2Json()方法的代码如下:

/*
    生成JSON
     */
    private void saveCrashInfo2Json() {
        String json = JSON.toJSONString(errorPram);
        String fileName = "crash.txt";
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            String path = Environment.getExternalStorageDirectory().getAbsolutePath();
            File dir = new File(path);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            try {
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(json.getBytes());
                fos.close();
            } catch (Exception e) {

            }
        }
    }

    在这里借助了第三方库fastjson将JavaBean对象转换为JsonString,存入text文档中。fastjson可以实现JsonObject、JavaBean以及JsonString三者之间的相互转化。同理,我们也可以使用它来进行Json文本的解析,这个在再次启动APP,需要将错误日志上传的时候用到。代码如下:

/*
   解析JSON,上传错误日志
    */
    private void newThread() {
        new Thread() {
            @Override
            public void run() {
                String pathname = Environment.getExternalStorageDirectory().getAbsolutePath() + "/救灾通/" + "crash.txt";
                final File file = new File(pathname); // 要读取以上路径的input。txt文件
                String text = "";
                if (file.exists()) {
                    try {
                        String encoding = "utf-8";
                        if (file.isFile() && file.exists()) { //判断文件是否存在
                            InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);//考虑到编码格式
                            BufferedReader bufferedReader = new BufferedReader(read);
                            String lineTxt = "";
                            while ((lineTxt = bufferedReader.readLine()) != null) {
                                text += lineTxt;
                            }
                            read.close();
                        } else {
                            return;
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    myError = JSON.parseObject(text, ErrorPram.class);
                } else {
                    return;
                }
            }
        }.start();
    }

    Fastjson API入口类是com.alibaba.fastjson.JSON,常用的序列化操作都可以在JSON类上的静态方法直接完成。
    public static final Object parse(String text); // 把JSON文本parse为JSONObject或者JSONArray 
    public static final JSONObject parseObject(String text); // 把JSON文本parse成JSONObject    
    public static final  T parseObject(String text, Class clazz); // 把JSON文本parse为JavaBean 
    public static final JSONArray parseArray(String text); // 把JSON文本parse成JSONArray 
    public static final  List parseArray(String text, Class clazz); //把JSON文本parse成JavaBean集合 
    public static final String toJSONString(Object object); // 将JavaBean序列化为JSON文本 
    public static final String toJSONString(Object object, boolean prettyFormat); // 将JavaBean序列化为带格式的JSON文本 
    public static final Object toJSON(Object javaObject); //将JavaBean转换为JSONObject或者JSONArray。

    最后是ExitAppUtils类:

import android.app.Activity;

import java.util.LinkedList;
import java.util.List;

/**
 * android退出程序的工具类,使用单例模式
 * 1.在Activity的onCreate()的方法中调用addActivity()方法添加到mActivityList
 * 2.你可以在Activity的onDestroy()的方法中调用delActivity()来删除已经销毁的Activity实例
 * 这样避免了mActivityList容器中有多余的实例而影响程序退出速度
 * @author xiaanming
 *
 */
public class ExitAppUtils {
    /**
     * 转载Activity的容器
     */
    private List mActivityList = new LinkedList();
    private static ExitAppUtils instance = new ExitAppUtils();

    /**
     * 将构造函数私有化
     */
    private ExitAppUtils(){};

    /**
     * 获取ExitAppUtils的实例,保证只有一个ExitAppUtils实例存在
     * @return
     */
    public static ExitAppUtils getInstance(){
        return instance;
    }
    
    /**
     * 添加Activity实例到mActivityList中,在onCreate()中调用
     * @param activity
     */
    public void addActivity(Activity activity){
        mActivityList.add(activity);
    }

    /**
     * 从容器中删除多余的Activity实例,在onDestroy()中调用
     * @param activity
     */
    public void delActivity(Activity activity){
        mActivityList.remove(activity);
    }
    
    /**
     * 退出程序的方法
     */
    public void exit(){
        for(Activity activity : mActivityList){
            activity.finish();
        }
        System.exit(0);
    }
}

    Android退出程序的工具类,使用单例模式。在Activity的onCreate()的方法中调用addActivity()方法添加到mActivityList;你可以在Activity的onDestroy()的方法中调用delActivity()来删除已经销毁的Activity实例,这样避免了mActivityList容器中有多余的实例而影响程序退出速度。

    到这里我们就可以实现全局捕获异常并上传的功能了。但我仍然有个问题不太懂,按道理说程序产生异常时直接上传就可以了,上传不成功时才会保存至本地。可是在实际过程中有时上传成功的,服务器返回的状态码也是200没问题,但前端死活接收不到服务器端的response,最后只能作罢,如有哪位大神知道恳请指点。

你可能感兴趣的:(Android开发,异常,Android,fastjson,JavaBean,Json)