Android全局异常捕获并弹窗提示

来源:互联网 发布:暴雪mac可以玩的游戏 编辑:程序博客网 时间:2024/05/16 10:23

  Android 难免有崩溃的时候,但是崩溃了该如何处理呢?虽然那天有位同仁说 “既然崩溃了,用户体验就差了,心里会想这是毛APP,下次也不想用了” ,所以检查BUG以防崩溃是必须的,但是也需要一个后备方案,崩溃了能友好些,我们也能收集一些崩溃的信息。

  说到全局捕获异常的UncaughtExceptionHandler,就不得不说期间遇到的各种坑:

  1. 初始化肯定在Application,网上说的Activity启各种不认同。但在Application启就存在不能弹AlertDialog的问题(目前不确定,不知道是自己哪里没处理好还是的确是这个问题,有时间再验证一下)

  2. 崩溃不一定是单次,在多层Activity中,崩溃一个顶层的Activity可能导致下层的Activity连续崩溃,所以uncaughtException可能会捕获到多次崩溃信息(具体影响后面会说到)

先来张崩溃后的效果图:

背景是另一个APP,当前的APP已崩溃并弹出该提示

崩溃后


实现流程:

写个类继承于UncaughtExceptionHandler,实现方法

    @Override    public void uncaughtException(Thread thread, Throwable ex) {        if (!handleException(ex) && mDefaultHandler != null) {            // 如果用户没有处理则让系统默认的异常处理器来处            mDefaultHandler.uncaughtException(thread, ex);        } else {            // 跳转到崩溃提示Activity            Intent intent = new Intent(mContext, CrashDialog.class);            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            mContext.startActivity(intent);            System.exit(0);// 关闭已奔溃的app进程        }    }

然后转handleException方法处理异常:

    private boolean handleException(Throwable ex) {        if (ex == null) {            return false;        }        // 收集错误信息        getCrashInfo(ex);        return true;    }

  上面的代码很清楚了,如果异常被捕获到并且异常信息不会NULL,处理完则跳转到CrashDialog。为什么跳Activity用Dialog样式,而不直接弹AlertDialog,是因为的确弹不出来。

收集错误信息:

private void getCrashInfo(Throwable ex) {        Writer writer = new StringWriter();        PrintWriter printWriter = new PrintWriter(writer);        ex.printStackTrace(printWriter);        Throwable cause = ex.getCause();        while (cause != null) {            cause.printStackTrace(printWriter);            cause = cause.getCause();        }        printWriter.close();        String errorMessage = writer.toString();        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {            String mFilePath = Environment.getExternalStorageDirectory() + "/" + App.ERROR_FILENAME;            FileTxt.WirteTxt(mFilePath, FileTxt.ReadTxt(mFilePath) + '\n' + errorMessage);        } else {            Log.i(App.TAG, "哦豁,说好的SD呢...");        }    }

  是的,我把错误信息写到了存储并在刚才的CrashDialog中读取。为什么不直接传值呢?因为刚说到的坑第2条,多次崩溃的情况下,将导致直接传值只会传最后一次崩溃信息,而最后一次崩溃信息并不是主要引发崩溃的点,收集上来的错误信息可读性不大。那为什么我不写个全局变量来存储呢?因为尝试过,不知道是机型问题(Huawei Mate7 - API 23)还是全部问题,变量压根就不记录数据,最后只有将信息依次写到存储。


主要代码

App.java

package cn.qson.androidcrash;/** *  @author x024 */import android.app.Application;public class App extends Application {    public final static String TAG = "x024";    public final static String ERROR_FILENAME = "x024_error.log";    @Override    public void onCreate() {        super.onCreate();        CrashHanlder.getInstance().init(this);    }}

CrashHanlder.java

package cn.qson.androidcrash;/** *  @author x024 */import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import java.lang.Thread.UncaughtExceptionHandler;import android.content.Context;import android.content.Intent;import android.os.Environment;import android.util.Log;/** * 收集错误报告并上传到服务器 *  * @author x024 * */public class CrashHanlder implements UncaughtExceptionHandler {    private Thread.UncaughtExceptionHandler mDefaultHandler;    // CrashHandler实例    private static CrashHanlder INSTANCE = new CrashHanlder();    // 程序的Context对象    private Context mContext;    private CrashHanlder() {    }    public static CrashHanlder getInstance() {        return INSTANCE;    }    /**     * 初始化     *      * @param context     */    public void init(Context context) {        mContext = context;        // 获取系统默认的UncaughtException处理        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();        // 设置该CrashHandler为程序的默认处理        Thread.setDefaultUncaughtExceptionHandler(this);    }    /**     * 异常捕获     */    @Override    public void uncaughtException(Thread thread, Throwable ex) {        if (!handleException(ex) && mDefaultHandler != null) {            // 如果用户没有处理则让系统默认的异常处理器来处            mDefaultHandler.uncaughtException(thread, ex);        } else {            // 跳转到崩溃提示Activity            Intent intent = new Intent(mContext, CrashDialog.class);            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);            mContext.startActivity(intent);            System.exit(0);// 关闭已奔溃的app进程        }    }    /**     * 自定义错误捕获     *      * @param ex     * @return true:如果处理了该异常信息;否则返回false.     */    private boolean handleException(Throwable ex) {        if (ex == null) {            return false;        }        // 收集错误信息        getCrashInfo(ex);        return true;    }    /**     * 收集错误信息     *      * @param ex     */    private void getCrashInfo(Throwable ex) {        Writer writer = new StringWriter();        PrintWriter printWriter = new PrintWriter(writer);        ex.printStackTrace(printWriter);        Throwable cause = ex.getCause();        while (cause != null) {            cause.printStackTrace(printWriter);            cause = cause.getCause();        }        printWriter.close();        String errorMessage = writer.toString();        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {            String mFilePath = Environment.getExternalStorageDirectory() + "/" + App.ERROR_FILENAME;            FileTxt.WirteTxt(mFilePath, FileTxt.ReadTxt(mFilePath) + '\n' + errorMessage);        } else {            Log.i(App.TAG, "哦豁,说好的SD呢...");        }    }}

CrashDialog.java

package cn.qson.androidcrash;/** *  @author x024 */import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;import android.app.Activity;import android.app.ActivityManager;import android.content.Context;import android.content.Intent;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class CrashDialog extends Activity {    private String mFilePath;    private Button btnExit, btnRestart;    private Boolean StorageState = false;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_crash);        CrashDialog.this.setFinishOnTouchOutside(false);        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {            mFilePath = Environment.getExternalStorageDirectory() + "/" + App.ERROR_FILENAME;            StorageState = true;        } else {            Log.i(App.TAG, "哦豁,说好的SD呢...");        }        new Thread(upLog).start();        initView();    }    private void initView() {        btnExit = (Button) findViewById(R.id.cash_exit);        btnRestart = (Button) findViewById(R.id.cash_restart);        btnExit.setOnClickListener(mOnClick);        btnRestart.setOnClickListener(mOnClick);    }    OnClickListener mOnClick = new OnClickListener() {        @Override        public void onClick(View v) {            switch (v.getId()) {            case R.id.cash_exit:                exit();                break;            case R.id.cash_restart:                restart();                break;            default:                break;            }        }    };    // 上传错误信息    Runnable upLog = new Runnable() {        @Override        public void run() {            try {                String Mobile = Build.MODEL;                String maxMemory = "" + getmem_TOLAL() / 1024 + "m";                String nowMemory = "" + getmem_UNUSED(CrashDialog.this) / 1024 + "m";                String eMessage = "未获取到错误信息";                if (StorageState) {                    eMessage = FileTxt.ReadTxt(mFilePath).replace("'", "");                }                Log.i(App.TAG, "Mobile:" + Mobile + " | maxMemory:" + maxMemory + " |nowMemory:" + nowMemory                        + " |eMessage:" + eMessage);                /**                 * 可以在这调你自己的接口上传信息                 */            } catch (Exception e) {                e.printStackTrace();            }        }    };    private void exit() {        FileTxt.deleteFile(mFilePath);        System.exit(0);        android.os.Process.killProcess(android.os.Process.myPid());    }    private void restart() {        Intent intent = getBaseContext().getPackageManager()                .getLaunchIntentForPackage(getBaseContext().getPackageName());        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);        startActivity(intent);        exit();    }    @Override    public void onBackPressed() {        super.onBackPressed();        exit();    }    // 获取可用内存    public static long getmem_UNUSED(Context mContext) {        long MEM_UNUSED;        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);        ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();        am.getMemoryInfo(mi);        MEM_UNUSED = mi.availMem / 1024;        return MEM_UNUSED;    }    // 获取剩余内存    public static long getmem_TOLAL() {        long mTotal;        String path = "/proc/meminfo";        String content = null;        BufferedReader br = null;        try {            br = new BufferedReader(new FileReader(path), 8);            String line;            if ((line = br.readLine()) != null) {                content = line;            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        } finally {            if (br != null) {                try {                    br.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        int begin = content.indexOf(':');        int end = content.indexOf('k');        content = content.substring(begin + 1, end).trim();        mTotal = Integer.parseInt(content);        return mTotal;    }}

完整代码:http://download.csdn.net/detail/hx7013/9710757

0 0
原创粉丝点击