转载请注明出处:http://blog.csdn.net/u014608640/article/details/52485340

现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设备上导致崩溃的信息,这对于下一个版本的bug修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序。

首先看一下效果图:

一。在MainActivity.java代码中

代码是这样写的:

public class MainActivity extends Activity {private TextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tv.setText("我没初始化");}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}

遇到软件没有捕获的异常之后,系统会弹出这个默认的强制关闭对话框。

我们当然不希望用户看到这种现象,简直是对用户心灵上的打击,而且对我们的bug的修复也是毫无帮助的。我们需要的是软件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发这分析出现异常的具体原因。

接下来我们就来实现这一机制,不过首先我们还是来了解以下两个类:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才会根据情况(Intent)来启动相应的Activity和Service。本示例中将在自定义加强版的Application中注册未捕获异常处理器。

Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。

大家刚才在项目的结构图中看到的CrashHandler.java实现了Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代码如下:

二:CrashHandler.java

public class CrashHandler implements UncaughtExceptionHandler {public static String TAG = "MyCrash";// 系统默认的UncaughtException处理类private Thread.UncaughtExceptionHandler mDefaultHandler;private static CrashHandler instance = new CrashHandler();private Context mContext;// 用来存储设备信息和异常信息private Map<String, String> infos = new HashMap<String, String>();// 用于格式化日期,作为日志文件名的一部分private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");/** 保证只有一个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);autoClear(5);}/*** 当UncaughtException发生时会转入该函数来处理*/@Overridepublic void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {// 如果用户没有处理则让系统默认的异常处理器来处理mDefaultHandler.uncaughtException(thread, ex);} else {SystemClock.sleep(1000);// 退出程序android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}}/*** 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.* * @param ex* @return true:如果处理了该异常信息; 否则返回false.*/private boolean handleException(Throwable ex) {if (ex == null)return false;try {// 使用Toast来显示异常信息new Thread() {@Overridepublic void run() {Looper.prepare();Toast.makeText(mContext, "很抱歉,程序出现异常,即将重启.",Toast.LENGTH_LONG).show();Looper.loop();}}.start();// 收集设备参数信息collectDeviceInfo(mContext);// 保存日志文件saveCrashInfoFile(ex);// 重启应用(按需要添加是否重启应用)
//            Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(mContext.getPackageName());
//            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
//            mContext.startActivity(intent);
//            SystemClock.sleep(1000);} catch (Exception e) {e.printStackTrace();}return true;}/*** 收集设备参数信息* * @param ctx*/public void collectDeviceInfo(Context ctx) {try {PackageManager pm = ctx.getPackageManager();PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),PackageManager.GET_ACTIVITIES);if (pi != null) {String versionName = pi.versionName + "";String versionCode = pi.versionCode + "";infos.put("versionName", versionName);infos.put("versionCode", versionCode);}} catch (NameNotFoundException e) {Log.e(TAG, "an error occured when collect package info", e);}Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);infos.put(field.getName(), field.get(null).toString());} catch (Exception e) {Log.e(TAG, "an error occured when collect crash info", e);}}}/*** 保存错误信息到文件中* @param ex* @return 返回文件名称,便于将文件传送到服务器* @throws Exception*/private String saveCrashInfoFile(Throwable ex) throws Exception {StringBuffer sb = new StringBuffer();try {SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date = sDateFormat.format(new java.util.Date());sb.append("\r\n" + date + "\n");for (Map.Entry<String, String> entry : infos.entrySet()) {String key = entry.getKey();String value = entry.getValue();sb.append(key + "=" + value + "\n");}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.flush();printWriter.close();String result = writer.toString();sb.append(result);String fileName = writeFile(sb.toString());return fileName;} catch (Exception e) {Log.e(TAG, "an error occured while writing file...", e);sb.append("an error occured while writing file...\r\n");writeFile(sb.toString());}return null;}private String writeFile(String sb) throws Exception {String time = formatter.format(new Date());String fileName = "crash-" + time + ".log";if (FileUtil.hasSdcard()) {String path = getGlobalpath();File dir = new File(path);if (!dir.exists())dir.mkdirs();FileOutputStream fos = new FileOutputStream(path + fileName, true);fos.write(sb.getBytes());fos.flush();fos.close();}return fileName;}public static String getGlobalpath() {return Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator + "crash" + File.separator;}public static void setTag(String tag) {TAG = tag;}/*** 文件删除* @param day 文件保存天数*/public void autoClear(final int autoClearDay) {FileUtil.delete(getGlobalpath(), new FilenameFilter() {@Overridepublic boolean accept(File file, String filename) {String s = FileUtil.getFileNameWithoutExtension(filename);int day = autoClearDay < 0 ? autoClearDay : -1 * autoClearDay;String date = "crash-" + DateUtil.getOtherDay(day);return date.compareTo(s) >= 0;}});}}

在收集异常信息时,朋友们也可以使用Properties,因为Properties有一个很便捷的方法properties.store(OutputStream out, String comments),用来将Properties实例中的键值对外输到输出流中,但是在使用的过程中发现生成的文件中异常信息打印在同一行,看起来极为费劲,所以换成Map来存放这些信息,然后生成文件时稍加了些操作。

完成这个CrashHandler后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application,添加自己的代码,CrashApplication.java代码如下:

三.MyAppcation

public class MyAppcation extends Application{@Overridepublic void onCreate() {super.onCreate();CrashHandler.getInstance().init(this);}}

因为我们上面的CrashHandler中,遇到异常后要保存设备参数和具体异常信息到SDCARD,所以我们需要在AndroidManifest.xml中加入读写SDCARD权限:

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

然后看一下SDCARD生成的文件:

源码下载地址:http://download.csdn.net/detail/u014608640/9626028

Android中处理崩溃异常和记录日志相关推荐

  1. Android记录日志方式,关于Android中处理崩溃异常和记录日志的另一种实现思路

    我们写程序的时候都希望能写出一个没有任何Bug的程序,期望在任何情况下都不会发生程序崩溃.不过理想是丰满的,现实是骨感的.没有一个程序员能保证自己写的程序绝对不会出现异常崩溃.特别是针对用户数达到几十 ...

  2. Android中处理崩溃异常

    2019独角兽企业重金招聘Python工程师标准>>> 大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象 ...

  3. android 中处理崩溃异常并重启程序

    转:http://blog.csdn.net/cym_lmy/article/details/24704089 有时候由于测试不充分或者程序潜在的问题而导致程序异常崩溃,这个是令人无法接受的,在and ...

  4. Android中处理崩溃异常 (转)

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

  5. Android application捕获崩溃异常

    Java代码 个人笔记: 通用 application 1.收集所有 avtivity 用于彻底退出应用 2.捕获崩溃异常,保存错误日志,并重启应用 public class HKBaseApplic ...

  6. Android中处理崩溃闪退错误

    我们需要的是软件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发这分析出现异常的具体原因.不过首先我们还是来了解以下两个类:androi ...

  7. android中的单例模式,Android中的单例模式

    定义: 单例模式:确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 使用场景: 确保某一个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一 ...

  8. android app无感知自动升级,Android中实现用户无感知处理后台崩溃

    正所谓,要想没有bug,就一行代码也不写.App到了用户的手里,肯定是崩溃越少越好.Android中的崩溃处理和iOS不太一样,iOS崩溃通常是闪退,而安卓会出现如下的蹩脚的对话框 image.png ...

  9. java的异常与记录日志

    今天在<java编程思想>一书中看到了异常与记录日志,发现学会将异常记录进日志中还是很有必要的,以下是书中的例子: 1 import java.io.PrintWriter; 2 impo ...

最新文章

  1. CreateStructuringElementEx
  2. MySQLRPM安装
  3. 使用NFS启动Tiny4412开发板根文件系统
  4. sixth week:third work
  5. 微软出面解释Win11各种大bug,引发网友一顿嘲讽:都是祖传手艺
  6. 如何评价三国里的袁绍
  7. 修改数据,如何将原数据带到输入框
  8. simditor存储数据到前台展示去掉标签
  9. Android呼出电话流程(原)
  10. 世纪联华的 Serverless 之路
  11. 数据库的这些性能优化,你做了吗
  12. linux查询日志中页面返回状态码,[linux shell] Shell脚本实现apache日志中的状态码分析...
  13. 阿里巴巴发布AliOS品牌 重投汽车及IoT领域
  14. 华为金融业务部总裁曹冲:没有进入支付市场的计划
  15. dreamweaver序列号免费_dreamweaver8【dreamweaver8序列号】dreamweaver8注册码序列号简体中文版...
  16. MySQL 批量插入申请自增 ID
  17. ubuntu版本查看命令
  18. Julia会是超越Python的存在吗
  19. 【cocos】Sprite九宫格拉伸SLICED
  20. win10在设备管理器里找不到蓝牙的三种解决办法

热门文章

  1. Unity-URP学习笔记(九)使用stencilmask制作哈利波特巧克力蛙巫师牌效果
  2. 从零开始Node.js—11mysql包的使用
  3. 超星图书压缩专用脚本程序
  4. angular的父子路由
  5. ios开发--用Xcode 8和Swift 3 构建条形码和二维码识别器
  6. 9.1、@keyframes关键帧
  7. Java循环语句详解(Java必备知识)
  8. 有一个程序员的老公,你们会担心老公失业吗?
  9. 文本三剑客之grep、sed、awk
  10. 一对多和多对多的区别