Android怎样捕获应用的crash信息

著论准过秦,作赋拟子虚。这篇文章主要讲述Android怎样捕获应用的crash信息相关的知识,希望能为你提供帮助。
转载请注明出处: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这种方法:

/** * Sets the default uncaught exception handler. This handler is invoked in * case any Thread dies due to an unhandled exception. * * @param handler *The handler to set or null. */ public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { Thread.defaultUncaughtHandler = handler; }


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

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


从源代码能够看出。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层的未捕获异常了。
这里给出一个大概的代码框架:

void sigkill_handler(int signo){ //打印堆栈,并写入到文件里 } void install(){ struct sigaction act, oldact; act.sa_handler = sigkill_handler; sigaddset(& act.sa_mask, SIGKILL); sigaction(SIGKILL, & act, & oldact); //注冊信号处理函数 ...... }


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

public class ErrorReporter implements UncaughtExceptionHandler {private final Application mContext; private final ReporterExecutor mReporterExecutor; ErrorReporter(Application context, boolean enabled) { mContext = context; final Thread.UncaughtExceptionHandler defaultExceptionHandler = Thread .getDefaultUncaughtExceptionHandler(); mReporterExecutor = new ReporterExecutor(context, defaultExceptionHandler); mReporterExecutor.setEnabled(enabled); Thread.setDefaultUncaughtExceptionHandler(this); }@Override public void uncaughtException(final Thread thread,final Throwable ex) { // TODO Auto-generated method stub LogUtil.i(AppCR.LOG_TAG," catch uncaughtException" ); mReporterExecutor.execute(thread, ex); }public void setEnabled(boolean enabled) { LogUtil.i(AppCR.LOG_TAG, " AppCR is" + (enabled ?
" enabled" : " disabled" ) + " for " + mContext.getPackageName()); } }


ReporterExecutor会调用Thread.setDefaultUncaughtExceptionHandler(this); 来改动默认的UncaughtExceptionHandler。当发生未捕获的异常时。调用mReporterExecutor.execute(thread,  ex); 来处理异常。
ReporterExecutor  中把异常信息以及操作系统的相关信息保存到文件里。

public class ReporterExecutor {public static final String TAG = ReporterExecutor.class.getSimpleName(); private Context mContext; private boolean mEnabled = false; private final Thread.UncaughtExceptionHandler mDefaultExceptionHandler; private File mCrashInfoFile; public ReporterExecutor(Context context, Thread.UncaughtExceptionHandler defaultedExceptionHandler) {mContext = context; mDefaultExceptionHandler = defaultedExceptionHandler; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { File path = Environment.getExternalStorageDirectory(); File dir = new File(path, " BleFairy" ); if (!dir.exists()) { dir.mkdirs(); }mCrashInfoFile = new File(dir, getCrashFileName()); if (!mCrashInfoFile.exists()) { try { mCrashInfoFile.createNewFile(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }public boolean isEnabled() { return mEnabled; }public void setEnabled(boolean enabled) { mEnabled = enabled; }public void execute(Thread thread, Throwable ex) {if (!mEnabled) { endApplication(thread, ex); return; }// log crash info to file Log.w(AppCR.LOG_TAG, " getSysInfo." ); CrashReportData data = https://www.songbingjia.com/android/CrashReportData.produce(thread, ex, mContext); data.writeToFile(mCrashInfoFile); endApplication(thread, ex); }private void endApplication(Thread thread, Throwable ex) {if (mDefaultExceptionHandler != null) { Log.w(AppCR.LOG_TAG, " execute default uncaughtException handler." ); mDefaultExceptionHandler.uncaughtException(thread, ex); } else { Log.w(AppCR.LOG_TAG, " kill process and exit." ); android.os.Process.killProcess(android.os.Process.myPid()); System.exit(10); } }private String getCrashFileName() { StringBuilder ret = new StringBuilder(); Calendar calendar = Calendar.getInstance(); ret.append(" crash_" ); ret.append(calendar.get(Calendar.YEAR)); int month = calendar.get(Calendar.MONTH)+1; int date = calendar.get(Calendar.DATE); if(month < 10 ){ ret.append(" 0" ); } ret.append(month); if(date< 10){ ret.append(" 0" ); } ret.append(date); ret.append(" .txt" ); return ret.toString(); } }


CrashReportData  类用于保存异常信息:

public class CrashReportData {private final String info; private CrashReportData(String crashInfo) { this.info = crashInfo; }public static CrashReportData produce(Thread thread, Throwable ex, Context context) {ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream print = new PrintStream(out); out.toString(); print.append(" crahtime:" + TimeUtil.getCurTimeString()).append(" \n" ); print.append(SysInfo.getSysInfo(context)).append(" \n" ); print.append(thread.getName()).append(" (threadID=" + thread.getId() + " )" ).append(" \n" ); print.append(ex.getMessage()).append(" \n" ); ex.printStackTrace(print); return new CrashReportData(out.toString()); }public void writeToFile(File file) { PrintWriter printer = null; try {// append to the end of crash file BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file, true)); printer = new PrintWriter(out); printer.println(info); printer.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally {if (printer != null) { printer.close(); } LogUtil.w(AppCR.LOG_TAG, " write exception info to file over." ); } }@Override public String toString() { // TODO Auto-generated method stub return info; // return super.toString(); }}


  SysIno类:

public class SysInfo {public static String getSysInfo(Context context) { StringBuilder info = new StringBuilder(); info.append(" osVersion=Android " ).append(Build.VERSION.RELEASE).append(" \n" ); info.append(" model=" ).append(Build.MODEL).append(" \n" ); info.append(" brand=" ).append(Build.BRAND).append(" \n" ); LogUtil.i(AppCR.LOG_TAG, " sys info collect over." ); return info.toString(); }}


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

public class AppCR { public static final String LOG_TAG=AppCR.class.getSimpleName(); private static ErrorReporter mErrorReporter; public static void init(Application application){ init(application,true); }public static void init(Application application,boolean enabled){ mErrorReporter = new ErrorReporter(application, enabled); } }


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

public class BeaconApplication extends Application {private final String TAG = " BeaconFairy.BeaconApplication" ; @Override public void onCreate() { super.onCreate(); AppCR.init(this,true); }}


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

< application android:name=" .BeaconApplication" android:allowBackup=" true" android:allowTaskReparenting=" true" android:icon=" @drawable/ic_launcher" android:label=" @string/app_name" android:theme=" @style/AppTheme" > ........ < /application>

申请写SD卡的权限:

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



【Android怎样捕获应用的crash信息】到此为止,我们自己定义的crash信息收集程序AppCR就完毕了。


    推荐阅读