项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都记录到文件存到本地用来分析问题。当然也可以在客户授权同意的情况下将这些错误信息或者缓存的文件发送到服务端进行分析。
本文只实现获取错误信息并存文件,如需上传请自己增加网络请求。
核心代码就是自己实现Thread.UncaughtExceptionHandler并重写uncaughtException方法,然后调用Thread的静态方法setDefaultUncaughtExceptionHandler将自己实现类对象设置进去,等程序发生crash闪退的时候系统会调用uncaughtException方法并传递错误信息。
第一步实现Thread.UncaughtExceptionHandler并完成信息保存:
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static final String CRASH_DIR = "crash";
private static final String FILE_NAME = "crash_log.txt";
private static final SimpleDateFormat DATE_FORMAT =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
private static final String TAG = "CrashHandler";
private Context mContext;
private Thread.UncaughtExceptionHandler mDefaultHandler;
public static void init(Context context) {
int i1 = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE);
int i2 = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (i1 == PackageManager.PERMISSION_GRANTED && i2 == PackageManager.PERMISSION_GRANTED) {
//已有权限
CrashHandler handler = new CrashHandler(context);
Thread.setDefaultUncaughtExceptionHandler(handler);
} else {
Log.e(TAG, "onClick: 无权限需申请");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// context.requestPermissions(permissions, 1000);
}else {
// 低版本设备
}
}
}
private CrashHandler(Context context) {
mContext = context.getApplicationContext();
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.e(TAG, "uncaughtException: 抛异常");
saveCrashInfo(ex);
mDefaultHandler.uncaughtException(thread, ex);
}
private void saveCrashInfo(Throwable ex) {
String time = DATE_FORMAT.format(new Date());
String deviceInfo = getDeviceInfo();
String crashInfo = getCrashInfo(ex);
File externalDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS+"/"+CRASH_DIR);
// File dir = new File(Environment.getExternalStorageDirectory(), CRASH_DIR);
if (!externalDir.exists()) {
externalDir.mkdirs();
}
// File dir = new File(Environment.getExternalStorageDirectory(), CRASH_DIR);
// if (!dir.exists()) {
// dir.mkdirs();
// }
File file = new File(externalDir, FILE_NAME);
try (PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file, true)))) {
pw.println("========== Crash Log ==========");
pw.println("Time: " + time);
pw.println(deviceInfo);
pw.println(crashInfo);
pw.println();
pw.println(); // 空行分隔每次崩溃记录
} catch (IOException e) {
e.printStackTrace();
}
}
private String getDeviceInfo() {
return "Model: " + Build.MODEL + "\n" +
"Brand: " + Build.BRAND + "\n" +
"Android SDK: " + Build.VERSION.SDK_INT + "\n" +
"Version: " + Build.VERSION.RELEASE;
}
private String getCrashInfo(Throwable ex) {
StringBuilder sb = new StringBuilder();
sb.append("Exception: ").append(ex.getClass().getName()).append("\n");
sb.append("Message: ").append(ex.getMessage()).append("\n");
StackTraceElement[] stackTrace = ex.getStackTrace();
for (StackTraceElement element : stackTrace) {
sb.append("\tat ").append(element.toString()).append("\n");
}
return sb.toString();
}
注意想创建文件夹要使用路径不能再是Environment.getExternalStorageDirectory()。否则可能无法成功指定到文件。
第二、实现类调用最好在Application的onCreate中实现,因为是全局的越早开始检测越好。
@Override
public void onCreate() {
super.onCreate();
context = this;
Log.e(TAG, "onCreate: ");
CrashHandler.init(this);
}
第三、主动创建一个异常
-
Log.e(TAG, "error: "+ 1/0 );
第四、实现效果: