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信息。具体应用测试就不举例了,因为这个很简单,随便故意抛出一个运行时异常就可以看到效果了。