注:知识来源于《android开发艺术探索》
android应用不可以避免的会发生crash,也称之为崩溃,无论你的程序写的多么完美,也无法完全避免crash的发生,可能是由于android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络状况。用户发生了crash,由于无法模拟,也无法知道用户当时的crash信息,所以往往也无能为力。幸运的是android提供了处理这类问题的方法:Thread.setDefaultUncaughtExceptionHandler();
当crash发生的时候,系统就会回调UncaughtExceptionHandler的uncaughtException方法,而我们可以将crash信息存储到sd卡,适时地上传,这样就可以分析crash的场景,从而在后续版本修复这个问题。
由于默认异常处理器是Thread类的静态成员,因此它的作用对象时当前进程的所有线程。
下面是一个典型的异常处理器的实现:
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Class description
* 崩溃处理类,用于捕获全局crash,生成日志,并上传服务器
*
* @author huburt
* @date 2016-11-08
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
//crash日志的存放位置
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/";
//crash日志文件的前缀
private static final String FILE_NAME = "crash";
//crash日志文件的后缀,本质是txt,可以改成任意后缀使用户无法阅读
private static final String FILE_NAME_SUFFIX = ".trace";
//默认的crashHandler,如没有则为null
private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
private Context mContext;
private static CrashHandler sInstance = new CrashHandler();
public static CrashHandler getInstance() {
return sInstance;
}
public void init(Context context) {
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
}
/**
* 当程序出现未被捕获的异常,系统将会自动调用此方法
*
* @param thread
* @param throwable
*/
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
dumpExceptionToSDCard(throwable);
uploadExceptionToServer();
} catch (Exception e) {
e.printStackTrace();
}
throwable.printStackTrace();
//如果系统提供了默认的异常处理器,则交给系统去结束程序,否则由自己结束
if (mDefaultCrashHandler != null) {
mDefaultCrashHandler.uncaughtException(thread, throwable);
} else {
Process.killProcess(Process.myPid());
}
}
/**
* 将crash日志写入sd卡
*
* @param throwable
*/
private void dumpExceptionToSDCard(Throwable throwable) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Log.d("tag", "sdcard unmounted");
return;
}
File dir = new File(PATH);
if (!dir.exists()) {
dir.mkdirs();
}
long current = System.currentTimeMillis();
String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date(current));
File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
try {
PrintWriter printWriter = new PrintWriter(new BufferedWriter(new FileWriter(file)));
printWriter.println(time);
dumpPhoneInfo(printWriter);
printWriter.println();
throwable.printStackTrace(printWriter);
printWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 写入手机环境信息
*
* @param printWriter
* @throws PackageManager.NameNotFoundException
*/
private void dumpPhoneInfo(PrintWriter printWriter) throws PackageManager.NameNotFoundException {
PackageManager packageManager = mContext.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
printWriter.print("App Version:");
printWriter.print(packageInfo.versionName);
printWriter.print("_");
printWriter.println(packageInfo.versionCode);
//android版本号
printWriter.print("Os Version:");
printWriter.print(Build.VERSION.RELEASE);
printWriter.print("_");
printWriter.println(Build.VERSION.SDK_INT);
//手机制造商
printWriter.print("Vendor:");
printWriter.println(Build.MANUFACTURER);
//手机型号
printWriter.print("Model:");
printWriter.println(Build.MODEL);
//cpu架构
printWriter.print("CPU ABI:");
printWriter.println(Build.CPU_ABI);
}
/**
* 将crash日志打包上传,此处省略
*/
private void uploadExceptionToServer() {
Log.d("tag", "upload");
}
}
如何使用那?也简单~
在Application初始化的时候为线程设置CrashHandler
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this);
}
}
最后别忘了在Manifest注册application类以及限权:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app.crashhandler" >
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:name=".App">
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>