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 = 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就完毕了。

时间: 2024-12-14 14:32:45

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

保留所有Android crash信息

保留所有Android crash信息 framework/base/core/java/com/android/internal/os/RuntimeInit.java 重新下面这个函数,加入自己的代码,把信息保存下来,或者发回服务器 */ private static class UncaughtHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwa

【Android】使用NDK定位Crash

有时app或者游戏突然崩溃只提示如下信息,要查找错误的原因感觉有点无从下手,因为提示信息太少了. 02-27 10:57:15.736: A/libc(32000): Fatal signal 11 (SIGSEGV) at 0x0000000c (code=1), thread 32014 (Thread-1461) 02-27 10:57:15.736: A/libc(32000): Send stop signal to pid:32000 in debugger_signal_handl

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

最近做一个项目,web端使用百度地图,PDA使用手持机自带的GPS定位系统获取经纬度,然后再百度地图上显示该经纬度会有一定距离的差异,这里就像可乐的瓶子拧上雪碧的盖子,能拧的上却不美观.所以为了数据的精确性,PDA端也用百度地图的接口. 下面主要流程分为以下几步: 新建项目BaiduMapAPIDemo. 下载百度地图 --Android定位SDK . Binging Labrary项目添加相关文件. 注册百度开发平台,创建应用,获取AK. 写入百度提供示例代码. 查看输出结果. 第一步:新建项

Android中一个有趣的crash的日志分析

很久前写的一篇文章,发出来以作纪念:) Android中一个有趣的crash的日志分析 首先看看bugly平台中异常的统计信息,表面上是一个NullPointerException: 发生异常设备统计信息如下图,有意思的是全部都是root过的机器: 接下来看跟踪日志,在最下面可以看到这样的日志,抛出了NullpointerException: 引起异常的是com.lishu.net.LishuNet$2类,从类名看显然是某一个类的内部类. 第一个反应,当然是搜索一下应用的源代码,看看是不是有co

关于获取Android系统所有已安装App信息的一些操作(详细)

转载请注明出处:http://blog.csdn.net/qinjuning       本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占用大小等.具体分为两个 部分,计划如下:  第一部分: 获取应用程序的packagename.label.icon等 :             第二部分:获取应用程序的占用大小,包括:缓存大小(cachsize).数据大小(datasize). 每部分都为您准备了简单丰富的实例,您一定不会错过.

Android:TextView显示富文本信息

最近需要在TextView中显示一些超链接等信息,如URL(点击后跳转),显示网络图片等. 整理如下: 1.显示URl,在需要显示URL超链接的TextView中设置 textview.setText(Html.fromHtml(urlString)) (urlString 用html语法来标识) versionText.setMovementMethod(LinkMovementMethod.getInstance()); 上面一句必须设置,否则将无法跳转. 2.显示网络图片 textview

android 安卓APP获取手机设备信息和手机号码的代码示例

下面我从安卓开发的角度,简单写一下如何获取手机设备信息和手机号码 准备条件:一部安卓手机.手机SIM卡确保插入手机里.eclipse ADT和android-sdk开发环境 第一步:新建一个android工程(JinshanTest), 并需要在工程的AndroidManifest.xml文件中,添加权限 <uses-permission android:name="android.permission.READ_PHONE_STATE"/> 图例: 第二步:新建一个工具类

android启动到kernel时log信息如下,出现重启现象

android启动到kernel时log信息如下,出现重启现象,请大神指导,谢谢 20160329_09:31:43[    2.164381]-(0)[0:swapper/0][HRTimer] 20160329_09:31:43[    2.164381] Occurs 0 times in last ISR duration 20160329_09:31:43[    2.164381] last fn:tick_sched_timer+0x0/0x80, dur:34461 ns (s:

Android进阶之获取APK签名信息及MD5指纹

1 Android进阶之获取APK签名信息及MD5指纹 package com.guesslive.caixiangji.util; public class SignUtils { private static final String ALGORITHM = "RSA"; private static final String SIGN_ALGORITHMS = "SHA1WithRSA"; private static final String DEFAULT