Android Crash 全局捕获
Android Crash 全局捕获
首先应该明白的一点是,Android在崩溃后会重新启动崩溃时的那个Activity,如果你的Activity在初始化的时候就直接崩溃,那么你将连续得到 Crash 崩溃日志.这个说出来可能没什么,可怜的我在看到崩溃日志时活脱脱的以为
uncaughtException(Thread thread, Throwable ex)方法被调用了两次.知道原因后就很好解决,在崩溃时把Activity栈清空就可以.
1. UncaughtExceptionHandler
这是一个接口,只有一个方法 uncaughtException(Thread t, Throwable e) 而且注释已经说明,这个方法是虚拟机(JVM)去调用.所以我们编写一个类实现此接口就可以了.
    public interface UncaughtExceptionHandler {
        /**
         * Method invoked when the given thread terminates due to the
         * given uncaught exception.
         * <p>Any exception thrown by this method will be ignored by the
         * Java Virtual Machine.
         * @param t the thread
         * @param e the exception
         */
        void uncaughtException(Thread t, Throwable e);
    }
2. 异常时的常规处理
1. 显示崩溃前的
Toast
2. 保存崩溃日志信息到sd卡
3. 重启APP
实际项目中,我们需要做好这三件事.
    /**
     * 异常发生时,系统回调的函数,我们在这里处理一些操作
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        showCrashToast();//展示崩溃前的Toast
        saveCrashReport2SD(mContext, ex);//保存崩溃信息到sdcard
        restartApp();//重新启动APP
    }
3. 完整的代码
需要注意的地方写在注释里了.
public class CrashHandler implements UncaughtExceptionHandler {
    private static final String TAG = "CrashHandler";
    private Context mContext;
    private static CrashHandler mInstance = new CrashHandler();
    private CrashHandler() {
    }
    /**
     * 单例模式,保证只有一个CrashHandler实例存在
     *
     * @return
     */
    public static CrashHandler getInstance() {
        return mInstance;
    }
    /**
     * 异常发生时,系统回调的函数,我们在这里处理一些操作
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        showCrashToast();
        saveCrashReport2SD(mContext, ex);
        restartApp();
    }
    /**
     * 为我们的应用程序设置自定义Crash处理
     */
    public void initCrashHandler(Context context) {
        mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(this);
    }
    private void restartApp() {
        Intent intent = new Intent(App.getInstance().getApplicationContext(),
                InitAdvsMultiActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent restartIntent = PendingIntent.getActivity(App.getInstance()
                .getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
        //重启应用
        AlarmManager mgr = (AlarmManager) App.getInstance().getSystemService(Context.ALARM_SERVICE);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis(), restartIntent);
        //清空Activity栈,防止系统自动重启至崩溃页面,导致崩溃再次出现.
        ActivityLifeManager.getInstance().finishAllActivity();
        //退出程序
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(0);
        System.gc();
    }
    private void showCrashToast() {
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(App.getInstance().getApplicationContext(), "很抱歉,程序出现异常,即将重启",
                        Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();
        try {
            Thread.sleep(1000);//Toast展示的时间
        } catch (InterruptedException e) {
        }
    }
    /**
     * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在LinkedHashMap中
     *
     * @param context
     * @return
     */
    private HashMap<String, String> obtainSimpleInfo(Context context) {
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        PackageManager mPackageManager = context.getPackageManager();
        PackageInfo mPackageInfo = null;
        try {
            mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(),
                    PackageManager.GET_ACTIVITIES);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        map.put("APP", "AppName");
        map.put("用户名", SharedPreferencesUtils.getInstance().getUserName());
        map.put("品牌", "" + Build.BRAND);
        map.put("型号", "" + Build.MODEL);
        map.put("SDK版本", "" + Build.VERSION.SDK_INT);
        map.put("versionName", mPackageInfo.versionName);
        map.put("versionCode", "" + mPackageInfo.versionCode);
        map.put("crash时间", parserTime(System.currentTimeMillis()));
        return map;
    }
    /**
     * 获取系统未捕捉的错误信息
     *
     * @param throwable
     * @return
     */
    private String obtainExceptionInfo(Throwable throwable) {
        StringWriter mStringWriter = new StringWriter();
        PrintWriter mPrintWriter = new PrintWriter(mStringWriter);
        throwable.printStackTrace(mPrintWriter);
        mPrintWriter.close();
        Log.e(TAG, mStringWriter.toString());
        return mStringWriter.toString();
    }
    /**
     * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中
     *
     * @param context
     * @param ex
     * @return
     */
    private String saveCrashReport2SD(Context context, Throwable ex) {
        String fileName = null;
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append(" = ").append(value).append("\n");
        }
        sb.append(obtainExceptionInfo(ex));
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            File dir = new File(CrashConstant.CRASH_DIR);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            try {
                fileName = dir.toString() + File.separator + parserTime(System.currentTimeMillis()) +".txt";
                FileOutputStream fos = new FileOutputStream(fileName);
                fos.write(sb.toString().getBytes());
                fos.flush();
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return fileName;
    }
    /**
     * 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式,并在后缀加入随机数
     *
     * @param milliseconds
     * @return
     */
    private String parserTime(long milliseconds) {
        System.setProperty("user.timezone", "Asia/Shanghai");
        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
        TimeZone.setDefault(tz);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        return format.format(new Date(milliseconds));
    }
}
Android Crash 全局捕获的更多相关文章
- Android进阶——Crash异常捕获并发送到服务器
		在项目中,我们常常会遇到Crash的现象,也就是程序崩溃的时候,这个时候最常看到的就是这个界面 如果你的项目已经发布到市场上了,这样的崩溃对于开发人员是看不到的,所以我们得想方法将崩溃信息发送到服务器 ... 
- Android  app 全局异常统一处理
		异常处理需求 Android app 出现 crash 时,会出现 "程序异常退出" 的提示并关闭,体验不好,另外主要是无法知道哪里出现的崩溃,需要知道哪里造成的异常,就需要一个全 ... 
- Android Crash 定位
		本文介绍了如何在 Android 手机发生 Crash 时进行 Log 分析的方法, 它可以帮助测试人员快速定位 Android 手机 Crash 发生的原因,同时给研发人员提供有效修改 Bug 的 ... 
- android中全局异常捕捉
		android中全局异常捕捉 只要写代码就会有bug,但是我们要想办法收集到客户的bug.有第三方bugly或者友盟等可以收集.但是,android原生就提供了有关收集异常的api,所以我们来学习一下 ... 
- Android获取全局Context的方法
		Android获取全局Context的方法 Android--应用全局获取Context - 超宇的博客 - CSDN博客https://blog.csdn.net/chaoyu168/article ... 
- 拖拽demo--兼容--全局捕获
		<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ... 
- 保留全部Android crash信息
		保留全部Android crash信息 framework/base/core/java/com/android/internal/os/RuntimeInit.java 又一次以下这个函数,增加自己 ... 
- 关于TornadoFx和Android的全局配置工具类封装实现及思路解析
		原文地址: 关于TornadoFx和Android的全局配置工具类封装实现及思路解析 - Stars-One的杂货小窝 目前个人开发软件存在设置页面,可以让用户自定义些设置,但我发现,存储数据的代码逻 ... 
- Android使用UncaughtExceptionHandler捕获全局异常
		Android系统的“程序异常退出”,给应用的用户体验造成不良影响.为了捕获应用运行时异常并给出友好提示,便可继承UncaughtExceptionHandler类来处理.通过Thread.setDe ... 
随机推荐
- 二维动态规划——Interleaving String
			97. Interleaving String Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2 ... 
- 求求别再这么用log4x了
			sl4j或者log4j中,推荐的记录方式是: private Logger log = Logger.getLogger(getClass()); //或者 private static final ... 
- line-height系列——定义和工作原理总结
			一.line-height的定义和工作原理总结 line-height的属性值: normal 默认 设置合理的行间距. number 设置数字,此数字会与当前的字体尺寸相乘来设置行间距li ... 
- JAVA finally字句的异常丢失和返回值覆盖解析
			转载:http://blog.csdn.net/sureyonder/article/details/5560538 Java虚拟机在每个try语句块和与其相关的catch子句的结尾 处都会“调用”f ... 
- windows服务用脚本无法启动
			1.创建windows服务工程 工程名:ServiceDemo 2.添加加载启动及卸载服务脚本 加载及启动批处理: @echo off if exist "%SystemRoot%/Micr ... 
- 【2(2N+1)魔方阵 】
			/* 2(2N+1)魔方阵 */ #include<stdio.h> #include<stdlib.h> #define N 6 #define SWAP(x, y) {in ... 
- matlab 自定义函数及调用
			这一篇博客主要学习了: 第一,自定义函数优点有很多,比如可重复利用:容易纠错,以后直接装入大工程里(更重要的是,实参是复制给形参的,在自定义函数中运行时形参独立显示,这一点和C不一样). 第二,通过h ... 
- 关于nodejs express4.X框架不支持layout模板的问题解决
			网上有有种方法是安装express-partials模块,然后在 app.set(‘view engine’, ‘ejs’); 这句后面加上app.use(partials());但是,经过我的反复尝 ... 
- linux php安装memcached扩展
			memcached的安装包括:1.服务端的安装:2.客户端的安装 一.服务器端安装 方法一: memcached 依赖于libevent 库,因此我们需要先安装libevent. 假设将源码放在/us ... 
- 社交媒体(朋友圈、微博、QQ空间)开发一网打尽,PC端移动端都有!——源码来袭!
			一.应用场景 曾几何时,社交媒体已经驻扎到了几乎每个人的生活中.看看你身边的朋友,有几个不玩朋友圈的?就算他不玩朋友圈,那也得玩微博吧.再没有底线,也得玩QQ空间. 不过,作为程序员的我们,没事还是少 ... 
