Java/Android 使用 UncaughtExceptionHandler 捕获未知的Exception

 

 

 

 

在Java or Android 开发中,会接触到各种Exception 先看一下分支结构:

 

 

 

顶级Throwable:

 

然后细分为Error(无法处理) 和Exception(可以处理)的分支

 

接触到最多的异常安装频率来说有这么几种:

NullPorinterException 空指针 访问的对象或者方法等为Null

ArrayIndexOutOfBoundsException  数组角标越界  比如size = 5 ,get(8),[8]

NumberFormatException  转换异常 比如 数据 "123a",通过Integer.valueOf()

ParseException 解析异常

ClassCastException 类型转换异常

ArithmeticException 算术异常 比如1/0

 

一旦异常发生就会发生不可期的后果,比如数据错误,程序崩溃,在Android中 如果异常存在,没有捕获可能APP直接就崩溃了 停止运行,用户体验极其不友好

怎么去处理比较优雅呢?

主角登场:--->   Thread.UncaughtExceptionHandler

 

通过源码可以看出这是个接口 需要实现它

然后 它有个方法需要实现:

 void uncaughtException(Thread t, Throwable e);

t代表线程,当前的线程 e是异常的一些信息

 

那么如何在Android中进行应用呢?

 

首先封装个管理类,做成单例模式,通过init 加载

private CarshHandlerMessage()
    {
    }

    public static CarshHandlerMessage getInstance()
    {
        return sInstance;
    }

    public void init(Context context)
    {
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
        mContext = context.getApplicationContext();
        intent = new Intent(mContext,CarshCauseActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }

 

这里用的是饿汉式,不过更推荐懒汉式写法 然后加同步

然后在实现的方法里写上回调和业务逻辑

    @Override
    public void uncaughtException(Thread t, Throwable e)
    {
        try
        {
            //保存到本地
            exportExceptionToSDCard(e);
            //下面也可以写上传的服务器的代码
        } catch (Exception e1)
        {
            e1.printStackTrace();
        }
        e.printStackTrace();
        //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就自己结束自己
        if (mDefaultCrashHandler != null)
        {
            mDefaultCrashHandler.uncaughtException(t, e);
        } 

    }

 

完整代码:

 


import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import com.example.qualityManagement.CarshCauseActivity;
import com.example.suppliermanagement.util.GlobalVar;
import com.google.gson.Gson;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;


public class CarshHandlerMessage implements Thread.UncaughtExceptionHandler
{

    private static final String       PATH             = Environment.getExternalStorageDirectory().getAbsolutePath();
    private static final String       FILE_NAME_SUFFIX = ".trace";
    private static       CarshHandlerMessage sInstance        = new CarshHandlerMessage();
    private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    private Context                         mContext;
    private Object sb;
    private Intent intent;
    private CarshHandlerMessage()
    {
    }

    public static CarshHandlerMessage getInstance()
    {
        return sInstance;
    }

    public void init(Context context)
    {
        mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
        mContext = context.getApplicationContext();
        intent = new Intent(mContext,CarshCauseActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }

    /**
     * 当程序中有未被捕获的异常,系统将会调用这个方法
     *
     * @param t 出现未捕获异常的线程
     * @param e 得到异常信息
     */
    @Override
    public void uncaughtException(Thread t, Throwable e)
    {
        try
        {
            //保存服务器
            exportExceptionToSDCard(e);
        } catch (Exception e1)
        {
            e1.printStackTrace();
        }
        e.printStackTrace();
        //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就自己结束自己
        if (mDefaultCrashHandler != null)
        {
            mDefaultCrashHandler.uncaughtException(t, e);
        } 

    }
    
    /**
     * 获取e.printStackTrace() 的具体信息,赋值给String 变量,并返回
     * 
     * @param e
     *            Exception
     * @return e.printStackTrace() 中 的信息
     */
    public static String getStackTraceInfo(Throwable e) {
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            e.printStackTrace(pw);//将出错的栈信息输出到printWriter中
            pw.flush();
            sw.flush();
            return sw.toString();
        } catch (Exception ex) {
            return "printStackTrace()转换错误";
        } finally {
            if (sw != null) {
                try {
                    sw.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
            if (pw != null) {
                pw.close();
            }
        }
 
    }

    /**
     * 导出异常信息提交
     *
     * @param e
     */
    private void exportExceptionToSDCard( final Throwable e)
    {
        new Thread(new Runnable() {
            
            @Override
            public void run() {
                try {
                    DeciveInfo info = appendPhoneInfo();
                    StringBuilder stb = new StringBuilder();
                    for(int x = 0 ; x < e.getStackTrace().length;x++){
                        stb.append(e.getStackTrace()[x]).append("\n");
                    }
                    stb.append(e.getMessage());
                    stb.append(getStackTraceInfo(e));
                    info.setErrMessage(stb.toString());
                    String json = new Gson().toJson(info);
                    String path = url;
                    URL url = new URL(path);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.addRequestProperty("msg", json);
                    connection.setRequestMethod("POST");
                    connection.setConnectTimeout(60000);
                    connection.setReadTimeout(60000);
                    connection.connect();
                    if(connection.getResponseCode() == 200){
                        connection.getInputStream();
                        mContext.startActivity(intent);
                    }
                } catch (NameNotFoundException e1) {
                    e1.printStackTrace();
                } catch (MalformedURLException e1) {
                    e1.printStackTrace();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }                
            }
        }).start();
        
    }

    /**
     * 获取手机信息
     */
    private DeciveInfo appendPhoneInfo() throws PackageManager.NameNotFoundException
    {
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
        DeciveInfo info = new DeciveInfo();
        info.setVersionCode(String.valueOf(pi.versionCode));
        info.setVersionName(pi.versionName);
        info.setAndroidVersion(String.valueOf(Build.VERSION.SDK_INT));
        info.setInfo(Build.MANUFACTURER + "\n"+Build.MODEL + "\n");

        return info;
    }
    public static class DeciveInfo {
        private String versionCode;
        private String versionName;
        private String date;
        private String errMessage;
        private String androidVersion;
        private String info;
        private String threadName;
        

        public String getThreadName() {
            return threadName;
        }

        public void setThreadName(String threadName) {
            this.threadName = threadName;
        }

        public String getVersionCode() {
            return versionCode;
        }

        public void setVersionCode(String versionCode) {
            this.versionCode = versionCode;
        }

        public String getVersionName() {
            return versionName;
        }

        public void setVersionName(String versionName) {
            this.versionName = versionName;
        }

        public String getDate() {
            return date;
        }

        public void setDate(String date) {
            this.date = date;
        }

        public String getErrMessage() {
            return errMessage;
        }

        public void setErrMessage(String errMessage) {
            this.errMessage = errMessage;
        }

        public String getAndroidVersion() {
            return androidVersion;
        }

        public void setAndroidVersion(String androidVersion) {
            this.androidVersion = androidVersion;
        }

        public String getInfo() {
            return info;
        }

        public void setInfo(String info) {
            this.info = info;
        }

    }
}

 

注意:

android 9.0上传信息需要加入网络动态权限

https://blog.csdn.net/qq_29769851/article/details/105663758