转载请注明出处: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 = 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" />

到此为止,我们自己定义的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. UML绘图总结

    九种图总算画完了,着实让自己纠结了老一阵子啊. 只是,幸运的是完毕了,尽管还有些不足之处,可是终于战胜它了.以下说一下自己的绘图过程  一.用例图 UML的第一幅图应该说是用例图了,这是我们绘图的前提 ...

  2. 一些VPS

    https://www.perfectip.net                                        5美元/1C/4G/20G/10Thttps://www.hetzne ...

  3. funuiTitle-居中问题修改

    今天遇到了一个问题,在一个actionbar上,title居中了,现在想要的方式是,让actionbar上显示返回按钮,后面紧跟着title.当时自己一直尝试要找到activity,然后在theme中 ...

  4. Python: scikit-image gamma and log 对比度调整

    这个函数,主要用来做对比度调整,利用 gamma 曲线 或者 log 函数曲线, gamma 函数的表达式: y=xγ, 其中, x 是输入的像素值,取值范围为 [0−1], y 是输出的像素值,通过 ...

  5. 63.当当网txt数据按行切割与合并

    获取文件有多少行 //获取文件有多少行 int getN(char *path) { FILE *pf = fopen(path, "r"); if (pf==NULL) { ; ...

  6. 公众平台调整SSL安全策略,请开发者注意升级

    公众平台调整SSL安全策略,请开发者注意升级 近一段时间HTTPS加密协议SSL曝出高危漏洞,可能导致网络中传输的数据被黑客监听,对用户信息.网络账号密码等安全构成威胁.为保证用户信息以及通信安全,微 ...

  7. 解读OpenRTB(实时竞价)生态系统

    最近3年,广告实时竞价(RealTimeBidding)模式逐渐流行起来. 2012年大致了解过,最近一段时间,重新温习下. 半壁江山 生态系统总的来说分为2个部分,卖方和买方. 卖方:媒体,即拥有广 ...

  8. 【hdu 1068】Girls and Boys

    [Link]:http://acm.hdu.edu.cn/showproblem.php?pid=1068 [Description] 有n个人,一些人认识另外一些人,选取一个集合,使得集合里的每个人 ...

  9. [JWT] JWT with HS256

    The advantages of JWT over traditional session based validation is: it effectively removing all auth ...

  10. Android学习笔记之Bitmap位图的缩放

    位图的缩放也可以借助Matrix或者Canvas来实现. 通过postScale(0.5f, 0.3f)方法设置旋转角度,然后用createBitmap方法创建一个经过缩放处理的Bitmap对象,最后 ...