全局异常处理

来源:互联网 发布:淘宝新店铺如何引流 编辑:程序博客网 时间:2024/05/21 19:03

在项目中,如果有异常未捕获,运行时触发异常,则会将异常抛出,给用户一个不好的体验。

虽然现在系统厂商都对未捕获异常进行了处理,基本不会发生弹出异常,崩溃退出应用的场景,但是我们在项目中一般还是会进行一个全局异常处理,可以自己定义或者使用第三方统计,如有盟统计,腾讯bugly等。

异常定义及分类

异常是指在程序运行过程中所出现的错误,这些错误会干扰到指令的正常执行,从而导致程序异常退出。

对java语言来说,所有异常都继承自Throwable

这里写图片描述

Error异常是我们无法处理的异常,属于系统异常。

Exception异常是由于我们代码编写失误造成的,可以通过修改代码进行控制。我们所处理的异常都属于此类异常。

异常根据触发时机不同又可分为两种:

  1. 编译时异常

    如ClassNotFoundException/CanNotFoundId layout未找到id

  2. 运行时异常

    如数组越界/类型转换异常

对异常的全局处理

/** * Interface for handlers invoked when a <tt>Thread</tt> abruptly * terminates due to an uncaught exception. * 当一个线程因为未捕获异常突然终止时调用*/public interface UncaughtExceptionHandler {    /**    *未捕获异常处理    */    void uncaughtException(Thread t, Throwable e);}

所以我们只需要实现UncaughtExceptionHandler中uncaughtException方法即可。

public class CrashHandler implements Thread.UncaughtExceptionHandler {    private static CrashHandler instance;    private Context mcontext;    private Thread.UncaughtExceptionHandler mDefualtHandller;    private Map<String, String> eMaps = new ArrayMap<>();    private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    private boolean isPrintLog;    private CrashHandler() {    }    public static CrashHandler getInstance() {        if (instance == null) {            synchronized (CrashHandler.class) {                if (instance == null) {                    instance = new CrashHandler();                }            }        }        return instance;    }    /**     * 初始化     * @param context     * @param isPrintLog 是否输出错误信息     */    public void init(Context context, boolean isPrintLog) {        mcontext = context;        this.isPrintLog = isPrintLog;        mDefualtHandller = Thread.getDefaultUncaughtExceptionHandler();        Thread.setDefaultUncaughtExceptionHandler(this);    }    /**     * 当触发未捕获异常时,系统将调用此方法     * @param t     * @param e     */    @Override    public void uncaughtException(Thread t, Throwable e) {        if (!handleException(e)) {            if (mDefualtHandller != null)                mDefualtHandller.uncaughtException(t, e);        } else {            try {                Thread.sleep(1000);            } catch (InterruptedException e1) {                e1.printStackTrace();            }            Process.killProcess(Process.myPid());            System.exit(1);        }    }    /**     * 是否手动处理未捕获异常     *     * @param e     * @return     */    private boolean handleException(Throwable e) {        if (e == null)            return false;        showExceptionMsg(e);        collectExceptionInfo();        saveExceptionInfo(e);        return true;    }    /**     * 收集异常信息     */    private void collectExceptionInfo() {        PackageManager pm = mcontext.getPackageManager();        try {            PackageInfo packageInfo = pm.getPackageInfo(mcontext.getPackageName(), PackageManager.GET_ACTIVITIES);            if (packageInfo != null) {                eMaps.put("versionName", packageInfo.versionName);                eMaps.put("versionCode", String.valueOf(packageInfo.versionCode));            }            saveDeviceInfo();        } catch (PackageManager.NameNotFoundException e) {            e.printStackTrace();        }    }    /**     * 保存设备信息     */    private void saveDeviceInfo() {        Field[] fields = Build.class.getFields();        if (fields != null && fields.length > 0)            for (Field field : fields) {                field.setAccessible(true);                try {                    eMaps.put(field.getName(), field.get(null).toString());                } catch (IllegalAccessException e) {                    e.printStackTrace();                }            }    }    /**     * 保存异常信息到sd卡     */    private void saveExceptionInfo(Throwable e) {        StringBuffer stringBuffer = new StringBuffer();        for (Map.Entry<String, String> entry : eMaps.entrySet()) {            stringBuffer.append(entry.getKey() + "===" + entry.getValue() + "\n\r");        }        Writer write = new StringWriter();        PrintWriter printer = new PrintWriter(write);        e.printStackTrace(printer);        Throwable cause = e.getCause();        while (cause != null) {            cause.printStackTrace(printer);            e.getCause();        }        printer.close();        stringBuffer.append(write.toString());        long time = System.currentTimeMillis();        String date = dateFormat.format(new Date());        String fileName = "crash-" + date + "-" + time + ".log";        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {            String filePath = "/sdcard/" + mcontext.getPackageName() + "-crash/";            File file = new File(filePath);            if (!file.exists())                file.mkdirs();            FileOutputStream fos = null;            try {                fos = new FileOutputStream(file + fileName);                fos.write(stringBuffer.toString().getBytes());            } catch (FileNotFoundException e1) {                e1.printStackTrace();            } catch (IOException e1) {                e1.printStackTrace();            } finally {                try {                    fos.close();                } catch (IOException e1) {                }            }        }        //TODO 将错误信息上次到服务器    }    /**     * 提示异常信息     */    private void showExceptionMsg(Throwable e) {        if (!isPrintLog) return;        if (Looper.getMainLooper() == Looper.myLooper())            showToast(e, true);        else            showToast(e, false);    }    private void showToast(Throwable e, boolean isMainThread) {        if (isMainThread)            Toast.makeText(mcontext, "未捕获异常:" + e.getMessage(), Toast.LENGTH_LONG).show();        else {            Looper.prepare();            Toast.makeText(mcontext, "未捕获异常:" + e.getMessage(), Toast.LENGTH_LONG).show();            Looper.loop();        }    }}

然后在Application中初始化即可。

public class App extends Application {    CrashHandler crashHandler;    @Override    public void onCreate() {        super.onCreate();        crashHandler=CrashHandler.getInstance();        crashHandler.init(this,BuildConfig.ISDEBUG);    }}

其中BuildConfig.ISDEBUG可以通过在build.gradle中配置

debug{        buildConfigField('boolean' , 'ISDEBUG', 'true')    }release {        buildConfigField('boolean' , 'ISDEBUG', 'false')        minifyEnabled false        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'    }

如果是字符串则需要 buildConfigField(‘String’ , ‘aaaa’, ‘\”fadfasfa\”’)

使用第三方统计

使用有盟统计或者腾讯bugly,按照文档设置完成之后,可以在后台看到异常报表

原创粉丝点击