学习笔记之Android利用UncaughtExceptionHandler捕获全局异常
来源:互联网 发布:中国轻工业出版社 知乎 编辑:程序博客网 时间:2024/06/04 20:58
一、概述
现如今,Android手机越发的普及,手机的品牌与型号五花八门、Android版本的不同,在开发过程中运行良好的app到了发布上线后安装到某款手机上说不定就出现异常崩溃的现象,开发者不可能在所有的设备逐个调试,所以在apk发布上线后,如果出现了崩溃现象,应及时捕获该设备导致崩溃的日志信息,这对于版本维护bug修复帮助极大,那么如何在app出现crash的情况下捕获设备的参数和较详细的异常信息,并将其上传到服务器供开发人员分析与修复。
二、UncaughtExceptionHandler
通常会导致程序崩溃的异常,这些异常不能被捕获到,利用Thread.UncaughtExceptionHandler就可以捕获到这些异常。从名字就可以看出来UncaughtExceptionHandler是针对某个线程而言的,由于在Android编程中,大量使用线程,如果统一处理呢?因为主线程只有一个,我们可以在主线程作处理。
三、原理
既然知道利用UncaughtExceptionHandler类来实现捕获全局异常,那么在Android SDK中进入UncaughtExceptionHandler.class里我们只需要关注以下函数
很显然,在初始化CrashHandler时:
1.获取系统默认的UncaughtException处理器(getDefaultUncaughtExceptionHandler())
2.设置该CrashHandler为程序的默认处理器(setDefaultUncaughtExceptionHandler())
3.当发生Crash时会转入UncaughtException函数来处理相应的业务代码
四、实现过程
关键代码CrashHandler类
public class CrashHandler implements UncaughtExceptionHandler { public static final String TAG = "CrashHandler"; //系统默认的UncaughtException处理类 private UncaughtExceptionHandler mDefaultHandler; //CrashHandler实例 private static CrashHandler INSTANCE = new CrashHandler(); //程序的Context对象 private Context mContext; //用于格式化日期,作为日志文件名的一部分 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); /** * 保证只有一个CrashHandler实例 */ private CrashHandler() { } /** * 获取CrashHandler实例 ,单例模式 */ public static CrashHandler getInstance() { return INSTANCE; } /** * 初始化 * * @param context */ public void init(Context context) { mContext = context; //获取系统默认的UncaughtException处理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 当UncaughtException发生时会转入该函数来处理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(3000); } catch (InterruptedException e) { Log.e(TAG, "error : ", e); } //退出程序 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(1); } } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. * * @param ex * @return true:如果处理了该异常信息;否则返回false. */ private boolean handleException(final Throwable ex) { if (ex == null) { return false; } final String strhh = saveCrashInfo2File(ex); Log.e(TAG, strhh); //使用Toast来显示异常信息 new Thread() { @Override public void run() { Looper.prepare(); Toast.makeText(mContext, strhh, Toast.LENGTH_LONG).show();// Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show(); Looper.loop(); } }.start(); //收集设备参数信息,保存日志文件 writeFileSdcardFile(FileUtils.SDPATH, "Crash_" + System.currentTimeMillis() + ".txt", saveCrashInfo2File(ex), ex.getMessage()); return true; } /** * 收集设备信息与错误日志 * * @param e */ public String saveCrashInfo2File(Throwable e) { StringBuilder sb = new StringBuilder(); sb.append("生产厂商:\n"); sb.append(Build.MANUFACTURER).append("\n\n"); sb.append("手机型号:\n"); sb.append(Build.MODEL).append("\n\n"); sb.append("系统版本:\n"); sb.append(Build.VERSION.RELEASE).append("\n\n"); sb.append("异常时间:\n"); sb.append(formatter.format(new Date())).append("\n\n"); sb.append("异常类型:\n"); sb.append(e.getClass().getName()).append("\n\n"); sb.append("异常信息:\n"); sb.append(e.getMessage()).append("\n\n"); sb.append("异常堆栈:\n"); Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); e.printStackTrace(printWriter); Throwable cause = e.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String result = writer.toString(); sb.append(result); return sb.toString(); } /** * 保存错误信息到文件中 * * @param path * @param fileName 文件名 * @param write_str 错误日志 * @param ex 错误信息 */ public void writeFileSdcardFile(String path, String fileName, String write_str, String ex) { if (!FileUtils.file.exists()) { FileUtils.CreateDir(); } try { FileOutputStream fout = new FileOutputStream(path + fileName); byte[] bytes = write_str.getBytes(); fout.write(bytes); fout.close(); Log.e(TAG, "保存成功" + path + fileName); //此地做上传错误日志代码// uploadLogFile(new File(path + fileName), ex); } catch (Exception e) { e.printStackTrace(); Log.e(TAG, "保存失败"); } }}
根据需求非debug版本,在BaseApplication类中初始化CrashHandler
FileUtils.CreateDir();//创建错误日志文件夹 if (CrashConfig.HAVE_LOG) { CrashHandler crashHandler = CrashHandler.getInstance(); crashHandler.init(this.getApplicationContext()); } }在某个Activity里人为制造crash来调试
boolean b = FileUtils.checkFilePathExists(FileUtils.SDPATH); StringBuffer buffer = new StringBuffer(); buffer.append("是否会生成错误日志:"+(CrashConfig.HAVE_LOG+"")) .append("\n\n") .append("当前编译模式:") .append(BuildConfig.DEBUG ? "debug模式" : "release模式") .append("\n\n") .append("存放错误日志文件夹是否存在:" + b) .append("\n\n") .append("存放错误日志文件夹物理路径:") .append("\n\n") .append(FileUtils.file.getAbsolutePath()); tv.setText(buffer); carsh.setText("点我崩溃"); carsh.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if(CrashConfig.HAVE_LOG){ Log.e("mjb", 5 % 0 + "");// throw new RuntimeException("这里是异常"); }else{ Toast.makeText(MainActivity.this,"无错误日志",Toast.LENGTH_SHORT).show(); } } });
五、小结
通过以上就可以捕获crash异常,这里的crash界面只是Toast,大家可根据需求优化发生crash时的友好界面以及将crash信息文件上传至后台服务器当中维护迭代等。
项目demo已经上传至GitHub 喜欢的star、fork。谢谢
- 学习笔记之Android利用UncaughtExceptionHandler捕获全局异常
- Android 之捕获全局异常之UncaughtExceptionHandler
- 利用UncaughtExceptionHandler捕获全局异常
- 捕获全局异常UncaughtExceptionHandler
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Android使用UncaughtExceptionHandler捕获全局异常
- Caffe之solver文件配置解读
- linux下gcc6.1.0编译器安装
- 安装wampserver出现“The Program can't start because MSVCR110.dll is missing from your computer. Try reinstalling the program to f
- 每天学习opensatck(10)
- Python初学者笔记(3):输出列表中的奇数/奇数项,字符串中的偶数项,字符串大小写转换
- 学习笔记之Android利用UncaughtExceptionHandler捕获全局异常
- hibernate
- BZOJ3511 土地划分
- 【React Native】源码分析之Native UI的封装和管理
- [生存志] 第98节 荀子谈性恶
- 总结HoloLens的一些小功能
- activity向另一个activity的fragment传值的问题
- yarn常用命令
- uva11021 Tribles