android CrashHandler处理异常

CrashHandler处理异常

情景

Android应用无法避免不会发生crash,可能属于系统底层的bug、或者机型适配、亦或者糟糕的网络环境。
当crash发生时,系统会kill我们的应用程序,会闪退或者提升用户程序已停止运行,而且更恐怖的是,对于开发者而言,是无法知道当时用户所操作或者面临的网络环境的,也无能为力的去解决改bug。
但是Android系统提供了这类问题的方法:UncaughtExceptionHandler

CrashHandler

 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);
    }

源码给出的定义如下:当一个线程由于未捕获异常而即将终止时,会回调这个接口。
当然有设置该接口的:

 public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        // Android-removed: SecurityManager stubbed out on Android
        /*
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(
                new RuntimePermission("setDefaultUncaughtExceptionHandler")
                    );
        }
        */

         defaultUncaughtExceptionHandler = eh;
     }

思路

根据上面的理解,我们可以设置一个UncaughtExceptionHandler接口。当系统发送crash时,就会回调uncaughtException接口,这是可以普获到异常信息了。
我们可以记录当前的崩溃信息,存在本地,记得记录当时硬件环境。再下次合理的时机将错误信息上传,对于缓存问题,如果存在脏数据导致的经常性崩溃,就可以强行清除本地缓存,也是一种避险手段。在上传服务器有几种选择:一是立即上传;二是等待应用空闲上传。

public class CrashHandler implements UncaughtExceptionHandler {

    private static final String TAG = "CrashHandler";
    private static final String INT_CRASH_TIME = "int_crash_time";

    private static final int MAX_CRASH_TIME = 3;

    private static CrashHandler instance;
    private Context mContext;
    private UncaughtExceptionHandler mOriginalHandler;

    public static CrashHandler getInstance() {
        if (instance == null) {
            instance = new CrashHandler();
        }
        return instance;
    }

    /**
     * 初始化
     */
    public void init(Context context) {
        mContext = context;
        mOriginalHandler = Thread.getDefaultUncaughtExceptionHandler();
        /**
         * 若打开L开关则小翼不打盹,反之则打盹
         * */
//        if (!L.isDebug) {
        //设置该CrashHandler为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
//        }
    }

    /**
     * 把崩溃信息放在sd卡缓存目录下,若没有sd卡,就放在data/data中的缓存目录下
     *
     * <p>SDCard/Android/data/你的应用包名/cache/<p/>
     */
    private static File getDiskCrashDir(Context context, String dirName) {
        String cachePath;
        if (context.getExternalCacheDir() != null) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getFilesDir().getPath();
        }
        return new File(cachePath + File.separator + dirName);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        try {
            Log.e("未捕获的异常", ex.toString());
            handleCrash();
            if (BuildConfig.DEBUG) {
                saveCrash2File(ex);
            }
            uploadExceptionToService(ex);

        } catch (Exception e) {
            Log.e("uncaughtException又抛出的异常",  ex.toString());
        }
        mOriginalHandler.uncaughtException(thread, ex);
    }

    private void uploadExceptionToService(Throwable ex) {
        //上传操作
        // 一定要获取一些硬件信息(在不侵犯隐私的前提下)
    }

    private void handleCrash() {
        if (MyApplication.getInstance() != null) {
            //elapsedTime 这个是 系统启动后到发送崩溃的时间
            final long elapsedTime = SystemClock.elapsedRealtime() - MyApplication.getInstance().getStartTime();
            Log.e(TAG, "elapsedTime:" + elapsedTime);
            //如果在启动后10s内就崩溃了,那就需要清除缓存了,很大概率就是缓存出现了脏数据,导致启动读取缓存数据就崩溃了。
            if (elapsedTime < 10 * 1000) {
                int time = (Integer) SPUtil.get(MyApplication.getInstance(),"app",INT_CRASH_TIME, 0);
                time += 1;
                Log.e(TAG, "崩溃次数" + time);
                if (time >= MAX_CRASH_TIME) {
                    Log.e(TAG, "崩溃次数达到上限,清除缓存");
                    //reset
                    SPUtil.put(MyApplication.getInstance(),"app",INT_CRASH_TIME, 0);
                    //到达上限 清除缓存相关数据
                    CacheUtil.clean();
                } else {
                    SPUtil.put(MyApplication.getInstance(),"app",INT_CRASH_TIME, time);
                }
            }
        } else {
            Log.e(TAG, "获取不到context");
        }
    }

    /**
     * 保存错误信息到文件中
     *
     * @param ex 崩溃信息
     * @throws IOException
     */
    public void saveCrash2File(Throwable ex) throws IOException {

        StringBuilder sb = new StringBuilder();
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause(); // 返回此异常的原因(尝试加载类时发生错误引发的异常;否则返回 null)
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try {
            // 用于格式化日期,作为日志文件名的一部分
            DateFormat formatter = new SimpleDateFormat("MM.dd@HH.mm", Locale.CHINA);
            String time = formatter.format(new Date());
            String fileName = time + ".log";
            File dir = getDiskCrashDir(mContext, "crash");
            if (!dir.exists()) {
                if (!dir.mkdirs()) {
                    return;
                }
            }
            String filePath = dir.getAbsolutePath() + File.separator + fileName;
            FileOutputStream fos = new FileOutputStream(filePath);
            fos.write(sb.toString().getBytes());
            fos.close();
        } catch (IOException e) {
            Log.e(TAG,e.toString());
        }
    }

}

在应用启动的时候初始化: CrashHandler.getInstance().init(getApplication());

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android崩溃收集可以通过以下步骤实现: 1. 在AndroidManifest.xml文件中添加以下权限: ``` <uses-permission android:name="android.permission.READ_LOGS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ``` 2. 在Application类中初始化崩溃捕获工具: ``` public class App extends Application { @Override public void onCreate() { super.onCreate(); CrashHandler.getInstance().init(this); } } ``` 3. 编写一个崩溃捕获工具类CrashHandler: ``` public class CrashHandler implements Thread.UncaughtExceptionHandler { private static CrashHandler instance = new CrashHandler(); private Thread.UncaughtExceptionHandler defaultHandler; private Context context; private CrashHandler() { } public static CrashHandler getInstance() { return instance; } public void init(Context context) { this.context = context; defaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread t, Throwable e) { if (!handleException(e) && defaultHandler != null) { defaultHandler.uncaughtException(t, e); } else { try { Thread.sleep(3000); } catch (InterruptedException ex) { ex.printStackTrace(); } android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } private boolean handleException(Throwable e) { if (e == null) { return false; } StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); String log = sw.toString(); Log.e("CrashHandler", log); saveLogToFile(log); return true; } private void saveLogToFile(String log) { String fileName = "crash-" + System.currentTimeMillis() + ".log"; try { File file = new File(Environment.getExternalStorageDirectory(), fileName); FileOutputStream fos = new FileOutputStream(file); fos.write(log.getBytes()); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 4. 在崩溃捕获工具类中,将崩溃信息输出到Logcat和文件中,方便查找和分析。 5. 最后,在应用程序崩溃时,CrashHandler将捕获异常并将其保存到文件中。可以通过查看这些文件来分析崩溃原因并进行修复。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值