最后一根救命稻草——CrashHandler

2016年余额已不足,在仅存一个多月的时间里,开启我的博客之旅。之前一直都想写来着,但都觉得写这东西太费时,所以一直都没敢提笔。就在自己独自一人在一个小黑屋里思考人生的时候,哎,慕名有种酸鼻的感觉让我发虚,好像自己是个小白(其实本来就是),整个年头下来,好像什么都没做,什么都没学。为了重振雄风,驱逐这自卑的心理,看来得把我所学的奇淫绝技整理下,做个笔录,以备不时之需。咸淡少扯,下面开始今天的主题
我们知道,不管程序怎么写都很难避免不crash,由于android机型实在TM多,或者是程序猿的代码逻辑有问题等。糟糕的是,很多时候产品发布后,如果用户在使用的时发生了crash,这个crash信息是很难获取到的,这非常不利于一个产品的持续发展。幸运的是,Android为我们提供了解决这类问题的方法,其实可以通过CrashHandler来监视应用的crash信息,这样当程序crash时就会调用CrashHandler的uncaughtException方法(这个稍后会说)。在这个方法中我们可以获取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;
}
从方法的定义来看好像可以设置系统的默认异常处理器,其实这个方法就可以解决上面所提到的crash问题。当crash方式的时候,系统就会回调UncaughtExceptionHandler的uncaughtException的方法,在uncaughtException方法中就可以获取到异常信息,可以选择把异常信息存储到SD卡中,然后在合适的时机通过网络将crash信息上传到服务器上,这样开发人员就可以分析用户crash的场景从而后面的版本中修复此类的crash。有了上面的分析,现在我们肯定知道获取crash信息的方式了。首先需要实现一个UncaughtExceptionHandler对象,在它的UncaughtException方法中获取异常信息并将其存储在SD卡中或者上传到服务器,然后调用Thread的setDefaultUncaughtExceptionHandler方法将它设置为线程默认异常处理器,由于默认异常处理器是Thead类的静态成员,因此它的作用对象是当前进程的所有线程。下面是一个典型的异常处理器的实现:
public class CrashHandler implements Thread.UncaughtExceptionHandler {

private static final String TAG = "CrashHandler";
private static final boolean DEBUG = true;
private static final String PATH = Environment.getExternalStorageDirectory()
        .getPath() + "/CrashHandler/log/";
private static final String FILE_NAME = "crash";
private static final String FILE_NAME_SUFFIX = ".trace";

private static CrashHandler sInstance = new CrashHandler();
private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
private Context mContext;

private CrashHandler(){}

public static CrashHandler getsInstance() {
    return sInstance;
}

public void init(Context context) {
    mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
    Thread.setDefaultUncaughtExceptionHandler(this);
    mContext = context.getApplicationContext();
}

/**
 * 当程序中有未捕获的异常,系统将自动调用uncaughtException方法,
 * Thread为出现未捕获异常的线程,ex为未捕获的异常,我们就可以
 * 得到异常信息
 * @param thread
 * @param ex
 */
@Override
public void uncaughtException(Thread thread, Throwable ex) {
    try {
        //导出信息到SD卡中
        dumpExceptionToSDCard(ex);
        //上传异常信息到服务器
        upLoadExceptionToServer();
    } catch (IOException e) {
        e.printStackTrace();
    }
    ex.printStackTrace();
    if (mDefaultCrashHandler != null) {
        mDefaultCrashHandler.uncaughtException(thread, ex);
    }else {
        Process.killProcess(Process.myPid());
    }

}

private void dumpExceptionToSDCard(Throwable ex) throws IOException{
    if (!Environment.getExternalStorageState().equals(Environment
            .MEDIA_MOUNTED)) {
        if (DEBUG) {
            Log.w(TAG, "sdcard unmout,skip dump exception");
            return;
        }
    }
    File dir = new File(PATH);
    if (!dir.exists()) {
        dir.mkdir();
    }
    long current = System.currentTimeMillis();
    String time = new SimpleDateFormat("yyyy-MM--dd HH:mm:ss").format(new Date(current));
    File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
    try {
        PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
        pw.println(time);
        dumpPHoneInfo(pw);
        pw.println();
        ex.printStackTrace();
        pw.close();
    } catch (IOException e) {
        Log.w(TAG, "dump crash info failed");
    }
}

private void dumpPHoneInfo(PrintWriter pw) {
    PackageManager pm = mContext.getPackageManager();
    PackageInfo pi = pm.getPackageArchiveInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
    pw.print("App Version:");
    pw.print(pi.versionName);
    pw.print("_");
    pw.println(pi.versionCode);
    //ANDROID 版本号
    pw.print("os version:");
    pw.print(Build.VERSION.RELEASE);
    pw.print("_");
    pw.println(Build.VERSION.SDK_INT);
    //手机制造商
    pw.print("Vendor:");
    pw.println(Build.MANUFACTURER);
    //手机型号
    pw.print("Model:");
    pw.println(Build.MODEL);
    //CPU架构
    pw.print("CPU ABI:");
    pw.println(Build.CPU_ABI);
}
private void upLoadExceptionToServer(){
    //TODO 上传到服务器
}

}
从上面的代码可以看出,当应用崩溃是,CrashHandler会将异常信息以及设备信息写入SD卡,接着将异常交给系统处理,系统会帮我们终止程序,如果系统没有默认的异常处理机制,那么就自行终止。当然我们可以选择异常信息上传到服务器。
如何使用上面的CrashHandler呢?,So Easy,可以选择在Application初始化的时候为线程设置CrashHandler,如下所示:
public class MyApplication extends Application{
private static MyApplication sInstance;
@Override
public void onCreate() {
super.onCreate();
sInstance = this;
CrashHandler crashHandler = CrashHandler.getsInstance();
crashHandler.init(this);
}
}
经过上面两个步骤,程序就可以处理未处理的异常了,就再也不拍程序crash了,同时还可以在服务器上查看用户的crash信息。具体应用测试就不举例了,因为这个很简单,随便故意抛出一个运行时异常就可以看到效果了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值