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

我们的应用不可避免的会发生crash,假设是在调试阶段,我们能够使用Logcat查看异常信息。可是假设应用公布之后呢?假设在用户那边crash了,假设我们能够捕获这些crash信息,那么对我们定位crash原因并修复问题是非常有帮助的。

应用crash就可以能是Java层的异常导致的,也可能是native层导致,以下分别来看一下该怎样处理。

1 Java层的未捕获异常处理

先来看一下Java层的crash信息收集吧。要想捕获Java层的crash信息并不难。Android已经提供了接口来帮助我们监控系统的未捕获的异常:使用Thread.setDefaultUncaughtExceptionHandler就能够让我们轻松的监控应用的不论什么意外crash。

首先来看一下Thread.setDefaultUncaughtExceptionHandler这种方法:

  1. /**
  2. * Sets the default uncaught exception handler. This handler is invoked in
  3. * case any Thread dies due to an unhandled exception.
  4. *
  5. * @param handler
  6. * The handler to set or null.
  7. */
  8. public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
  9. Thread.defaultUncaughtHandler = handler;
  10. }

从Thread.setDefaultUncaughtExceptionHandler这种方法的凝视就能够看到:当进程内(由于Thread.defaultUncaughtHandler 是一个静态变量,因此对整个进程内的全部线程有效)的不论什么线程发生未捕获异常时。会调用这里设置的handler。那我们看一下UncaughtExceptionHandler 这个类吧:

  1. /**
  2. * Implemented by objects that want to handle cases where a thread is being
  3. * terminated by an uncaught exception. Upon such termination, the handler
  4. * is notified of the terminating thread and causal exception. If there is
  5. * no explicit handler set then the thread's group is the default handler.
  6. */
  7. public static interface UncaughtExceptionHandler {
  8. /**
  9. * The thread is being terminated by an uncaught exception. Further
  10. * exceptions thrown in this method are prevent the remainder of the
  11. * method from executing, but are otherwise ignored.
  12. *
  13. * @param thread the thread that has an uncaught exception
  14. * @param ex the exception that was thrown
  15. */
  16. void uncaughtException(Thread thread, Throwable ex);
  17. }

从源代码能够看出。UncaughtExceptionHandler 事实上是一个接口,它仅仅定义了一个方法uncaughtException(Thread thread, Throwable ex),当线程由于遇到未捕获异常而终止的时候就会调用这种方法。

假设我们想要捕获应用的crash信息,那么定义一个自己的UncaughtExceptionHandler 就能够,当然我们须要在自己的UncaughtExceptionHandler 里面把crash信息保存起来,必要的时候还能够上传到我们的server,这样就能够非常方便的收集用户的crash信息。

2 native层的异常处理

假设我们的应用使用到c/c++,那么也须要收集native层的异常处理。

大家都知道。Android的底层是基于Linux的,那么native层的未捕获异常就能够通过捕获信号来处理了。Native层假设异常终止会发出SIGKILL信号。我们能够使用sigaaction来注冊一个信号处理函数来处理SIGKILL信号,这样就能够收集到native层的未捕获异常了。

这里给出一个大概的代码框架:

  1. void sigkill_handler(int signo){
  2. //打印堆栈,并写入到文件里
  3. }
  4. void install(){
  5. struct sigaction act, oldact;
  6. act.sa_handler = sigkill_handler;
  7. sigaddset(&act.sa_mask, SIGKILL);
  8.  
  9. sigaction(SIGKILL, &act, &oldact);//注冊信号处理函数
  10. ......
  11. }

3 实现

结合上面的介绍。以下就来定义一个自己的UncaughtExceptionHandler 。这个样例仅仅处理了Java层的crash收集,并把收集到的crash信息保存到sd卡上。这里给我们自己定义的crash处理器起了一个名字叫做AppCR(Application Crash Response)。

首先定义ErrorReporter ,它实现了UncaughtExceptionHandler :

  1. public class ErrorReporter implements UncaughtExceptionHandler {
  2.  
  3. private final Application mContext;
  4. private final ReporterExecutor mReporterExecutor;
  5.  
  6. ErrorReporter(Application context, boolean enabled) {
  7. mContext = context;
  8.  
  9. final Thread.UncaughtExceptionHandler defaultExceptionHandler = Thread
  10. .getDefaultUncaughtExceptionHandler();
  11. mReporterExecutor = new ReporterExecutor(context, defaultExceptionHandler);
  12. mReporterExecutor.setEnabled(enabled);
  13. Thread.setDefaultUncaughtExceptionHandler(this);
  14. }
  15.  
  16. @Override
  17. public void uncaughtException(final Thread thread,final Throwable ex) {
  18. // TODO Auto-generated method stub
  19. LogUtil.i(AppCR.LOG_TAG,"catch uncaughtException");
  20.  
  21. mReporterExecutor.execute(thread, ex);
  22. }
  23.  
  24. public void setEnabled(boolean enabled) {
  25. LogUtil.i(AppCR.LOG_TAG, "AppCR is" + (enabled ?
  26.  
  27. "enabled" : "disabled") + " for "
  28. + mContext.getPackageName());
  29. }
  30. }

ReporterExecutor会调用Thread.setDefaultUncaughtExceptionHandler(this);来改动默认的UncaughtExceptionHandler。当发生未捕获的异常时。调用mReporterExecutor.execute(thread, ex);来处理异常。

ReporterExecutor 中把异常信息以及操作系统的相关信息保存到文件里。

  1. public class ReporterExecutor {
  2.  
  3. public static final String TAG = ReporterExecutor.class.getSimpleName();
  4. private Context mContext;
  5. private boolean mEnabled = false;
  6. private final Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
  7. private File mCrashInfoFile;
  8.  
  9. public ReporterExecutor(Context context,
  10. Thread.UncaughtExceptionHandler defaultedExceptionHandler) {
  11.  
  12. mContext = context;
  13. mDefaultExceptionHandler = defaultedExceptionHandler;
  14.  
  15. if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
  16. File path = Environment.getExternalStorageDirectory();
  17. File dir = new File(path, "BleFairy");
  18. if (!dir.exists()) {
  19. dir.mkdirs();
  20. }
  21.  
  22. mCrashInfoFile = new File(dir, getCrashFileName());
  23. if (!mCrashInfoFile.exists()) {
  24. try {
  25. mCrashInfoFile.createNewFile();
  26. } catch (IOException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. }
  31. }
  32. }
  33.  
  34. public boolean isEnabled() {
  35. return mEnabled;
  36. }
  37.  
  38. public void setEnabled(boolean enabled) {
  39. mEnabled = enabled;
  40. }
  41.  
  42. public void execute(Thread thread, Throwable ex) {
  43.  
  44. if (!mEnabled) {
  45. endApplication(thread, ex);
  46. return;
  47. }
  48.  
  49. // log crash info to file
  50. Log.w(AppCR.LOG_TAG, "getSysInfo.");
  51. CrashReportData data = CrashReportData.produce(thread, ex, mContext);
  52. data.writeToFile(mCrashInfoFile);
  53. endApplication(thread, ex);
  54.  
  55. }
  56.  
  57. private void endApplication(Thread thread, Throwable ex) {
  58.  
  59. if (mDefaultExceptionHandler != null) {
  60. Log.w(AppCR.LOG_TAG, "execute default uncaughtException handler.");
  61. mDefaultExceptionHandler.uncaughtException(thread, ex);
  62. } else {
  63. Log.w(AppCR.LOG_TAG, "kill process and exit.");
  64. android.os.Process.killProcess(android.os.Process.myPid());
  65. System.exit(10);
  66. }
  67. }
  68.  
  69. private String getCrashFileName() {
  70. StringBuilder ret = new StringBuilder();
  71. Calendar calendar = Calendar.getInstance();
  72.  
  73. ret.append("crash_");
  74. ret.append(calendar.get(Calendar.YEAR));
  75. int month = calendar.get(Calendar.MONTH)+1;
  76. int date = calendar.get(Calendar.DATE);
  77. if(month < 10 ){
  78. ret.append("0");
  79. }
  80. ret.append(month);
  81. if(date<10){
  82. ret.append("0");
  83. }
  84. ret.append(date);
  85. ret.append(".txt");
  86. return ret.toString();
  87. }
  88. }

CrashReportData 类用于保存异常信息:

  1. public class CrashReportData {
  2.  
  3. private final String info;
  4.  
  5. private CrashReportData(String crashInfo) {
  6. this.info = crashInfo;
  7. }
  8.  
  9. public static CrashReportData produce(Thread thread, Throwable ex, Context context) {
  10.  
  11. ByteArrayOutputStream out = new ByteArrayOutputStream();
  12. PrintStream print = new PrintStream(out);
  13. out.toString();
  14.  
  15. print.append("crahtime:" + TimeUtil.getCurTimeString()).append("\n");
  16. print.append(SysInfo.getSysInfo(context)).append("\n");
  17. print.append(thread.getName()).append("(threadID=" + thread.getId() + ")").append("\n");
  18. print.append(ex.getMessage()).append("\n");
  19. ex.printStackTrace(print);
  20.  
  21. return new CrashReportData(out.toString());
  22. }
  23.  
  24. public void writeToFile(File file) {
  25. PrintWriter printer = null;
  26. try {
  27.  
  28. // append to the end of crash file
  29. BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file, true));
  30. printer = new PrintWriter(out);
  31. printer.println(info);
  32. printer.flush();
  33.  
  34. } catch (IOException e) {
  35. // TODO Auto-generated catch block
  36. e.printStackTrace();
  37.  
  38. } finally {
  39.  
  40. if (printer != null) {
  41. printer.close();
  42. }
  43. LogUtil.w(AppCR.LOG_TAG, "write exception info to file over.");
  44.  
  45. }
  46. }
  47.  
  48. @Override
  49. public String toString() {
  50. // TODO Auto-generated method stub
  51. return info;
  52. // return super.toString();
  53. }
  54.  
  55. }

SysIno类:

  1. public class SysInfo {
  2.  
  3. public static String getSysInfo(Context context) {
  4. StringBuilder info = new StringBuilder();
  5. info.append("osVersion=Android ").append(Build.VERSION.RELEASE).append("\n");
  6. info.append("model=").append(Build.MODEL).append("\n");
  7. info.append("brand=").append(Build.BRAND).append("\n");
  8.  
  9. LogUtil.i(AppCR.LOG_TAG, "sys info collect over.");
  10. return info.toString();
  11. }
  12.  
  13. }

使用AppCR来安装我们的crash处理器:

  1. public class AppCR {
  2. public static final String LOG_TAG=AppCR.class.getSimpleName();
  3. private static ErrorReporter mErrorReporter;
  4.  
  5. public static void init(Application application){
  6. init(application,true);
  7. }
  8.  
  9. public static void init(Application application,boolean enabled){
  10. mErrorReporter = new ErrorReporter(application, enabled);
  11. }
  12. }

Application中安装上面自己定义的AppCR就能够了:

  1. public class BeaconApplication extends Application {
  2.  
  3. private final String TAG = "BeaconFairy.BeaconApplication";
  4.  
  5. @Override
  6. public void onCreate() {
  7. super.onCreate();
  8. AppCR.init(this,true);
  9. }
  10.  
  11. }

须要注意的是:我们须要定义自己的Application,然后改动manifest就能够啦,还要记得加上写SD卡的权限:

  1. <application
  2. android:name=".BeaconApplication"
  3. android:allowBackup="true"
  4. android:allowTaskReparenting="true"
  5. android:icon="@drawable/ic_launcher"
  6. android:label="@string/app_name"
  7. android:theme="@style/AppTheme" >
  8. ........
  9. </application>

申请写SD卡的权限:

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

到此为止,我们自己定义的crash信息收集程序AppCR就完毕了。

Android怎样捕获应用的crash信息的更多相关文章

  1. android 之 Crash信息的持久化处理

    需求: 持久化运行时异常的信息 1.CrashHandler.java import android.content.Context; import android.content.pm.Packag ...

  2. 保留全部Android crash信息

    保留全部Android crash信息 framework/base/core/java/com/android/internal/os/RuntimeInit.java 又一次以下这个函数,增加自己 ...

  3. Java & Android未捕获异常处理机制

    一.背景 无论是Java还是Android项目,往往都会用到多线程.不管是主线程还是子线程,在运行过程中,都有可能出现未捕获异常.未捕获异常中含有详细的异常信息堆栈,可以很方便的去帮助我们排查问题. ...

  4. 使用CrashHandler获取应用crash信息

      Android应用不可避免会发生crash,也称之为崩溃.发生原因可能是由于Android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络情况.当crash发生时,系统会kill掉正 ...

  5. Xamarin.Android 使用百度地图获取定位信息

    最近做一个项目,web端使用百度地图,PDA使用手持机自带的GPS定位系统获取经纬度,然后再百度地图上显示该经纬度会有一定距离的差异,这里就像可乐的瓶子拧上雪碧的盖子,能拧的上却不美观.所以为了数据的 ...

  6. 【转】android 安卓APP获取手机设备信息和手机号码的代码示例

    http://blog.csdn.net/changemyself/article/details/7421476 下面我从安卓开发的角度,简单写一下如何获取手机设备信息和手机号码 准备条件:一部安卓 ...

  7. Android 向系统添加一个联系人信息contact

    private void writeContacts() { Uri rawContacts = Uri.parse("content://com.android.contacts/raw_ ...

  8. Android的5样的调试信息

    Android的5样的调试信息 华清2014-10-23   北京海淀区  张俊浩 verbose:只是滤全部的信息. 啰嗦的意思. debug:debug调试的意思. info:一般提示的信息inf ...

  9. Android Text Color设置不当造成信息不显示

    Android Text Color设置不当造成信息不显示 Android 的TextView 可以设置颜色,默认我们可以设置成 #000000 ,但某一次设置成了#00000000 ,就是多了两个0 ...

随机推荐

  1. Ternary Tree

    前一篇文章介绍了Trie树.它实现简单但空间效率低.假设要支持26个英文字母,每一个节点就要保存26个指针,因为节点数组中保存的空指针占用了太多内存.让我来看看Ternary Tree. When y ...

  2. POJ--2516--Minimum Cost【最小费用最大流】

    链接:http://poj.org/problem?id=2516 题意:有k种货物,n个客户对每种货物有一定需求量,有m个仓库.每一个仓库里有一定数量的k种货物.然后k个n*m的矩阵,告诉从各个仓库 ...

  3. windows 静态和动态库

    c++中共有两种库:1.动态链接库LIB包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library.(这 ...

  4. 洛谷 P3654 First Step (ファーストステップ)

    洛谷 P3654 First Step (ファーストステップ) https://www.luogu.org/problemnew/show/P3654 题目描述 可是……这个篮球场,好像很久没有使用过 ...

  5. 【Uva 11080】Place the Guards

    [Link]: [Description] 一些城市,之间有道路相连,现在要安放警卫,警卫能看守到当前点周围的边,一条边只能有一个警卫看守,问是否有方案,如果有最少放几个警卫. [Solution] ...

  6. Moodle 中文 API 之 文件管理API

    File API  文件管理 文件夹 1. 概述 2. 文件域 2.1 命名文件域 3. 提供文件给用户 4. 从用户那获取文件 5. 样例 5.1 浏览文件 5.2 移动文件 5.3 文件列表 5. ...

  7. Excel查询序列所相应的值-vLoopup函数,求比例分子改变但分母不变

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveWV3ZWlvdXlhbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  8. 关于android主线程异常NetworkOnMainThread不能訪问网络

    今天在学习的过程中遇到了NetworkOnMainThread的异常,关于这个异常问题在android sdk 4.0版本号上,这个问题可能比較常见,查了许些资料大多都是大概解说原因,可是没有解说到详 ...

  9. 16、cgminer学习之:pthread_mutex_init和pthread_cond_init

    1.原理 假设有两个线程同时访问一个全局变量 n,这个全局变量的初始值等于0. Int  n = 0 ; 消费者线程 A 进入临界区,访问 n,A 必须等到 n 大于 0 才能接着往下执行,如果 n= ...

  10. Leetcode-求两数之和

    题目: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数组中 ...