android 捕获未try的异常、抓取崩溃日志

1.Thread.UncaughtExceptionHandler

  java里有很多异常如:空指针异常,越界异常,数值转换异常,除0异常,数据库异常等等。如果自己没有try / catch 那么线程就崩溃。

  并不能对所有代码都try/catch,如果代码产生了未捕获的异常,又不想让程序崩溃,或者在崩溃之前要做一些收尾工作。怎么办?

  Thread.UncaughtExceptionHandler 类可以解决这个问题,当有未捕获异常时,它的 public void uncaughtException(Thread t, Throwable e) 方法会被调用,参数包含了崩溃的线程及相应的异常信息。

  Thread类中的 public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法指定接收未捕获异常处理类。通常在 Application 里指定一个生命周期很长的未捕获异常处理类。

2.示例

2.1 自定义Thread.UncaughtExceptionHandler

 1 import android.content.Context
 2 import android.content.pm.PackageManager
 3 import android.os.Build
 4 import java.io.File
 5 import java.io.PrintWriter
 6 import java.text.SimpleDateFormat
 7 import java.util.*
 8 
 9 class CrashHandler(val context: Context) : Thread.UncaughtExceptionHandler {
10 
11     private lateinit var default : Thread.UncaughtExceptionHandler
12     override fun uncaughtException(t: Thread, e: Throwable) {
13         e.printStackTrace()
14         writeLog(t,e)
15         if (::default.isInitialized){
16             default.uncaughtException(t,e)
17         }
18     }
19 
20     fun start(){
21         default = Thread.getDefaultUncaughtExceptionHandler()
22         Thread.setDefaultUncaughtExceptionHandler(this)
23     }
24 
25     fun writeLog(t: Thread, e: Throwable){
26         val log = context.getExternalFilesDir("")?.absolutePath + "/crash.log"
27         val time = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date())
28         try {
29             val file = File(log)
30             val pw = PrintWriter(file,"UTF-8")
31             pw.println("time        : $time")
32             pw.println("Thread      : name = ${t.name} ,priority = ${t.priority} , pid = ${t.id}")
33             writePhoneInfo(pw)
34             e.printStackTrace(pw)
35             pw.close()
36         }catch (e : Exception){
37             e.printStackTrace()
38         }
39     }
40     fun writePhoneInfo(pw : PrintWriter) {
41         val pm = context.getPackageManager()
42         val pi = pm.getPackageInfo(context.packageName,PackageManager.GET_ACTIVITIES)
43 
44         pw.println("app version : ${pi.versionName}")
45         pw.println("android ver : ${Build.VERSION.RELEASE}")
46         pw.println("sdk version : ${Build.VERSION.SDK_INT}")
47         pw.println("product     : ${Build.PRODUCT}")
48 
49         pw.println("vendor      : ${Build.MANUFACTURER}")
50         pw.println("brand       : ${Build.BRAND}")
51         pw.println("model       : ${Build.MODEL}")
52         pw.println("hardware    : ${Build.HARDWARE}")
53 
54 
55         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
56             var abis = ""
57             for (abi in Build.SUPPORTED_ABIS){
58                 abis += "$abi,"
59             }
60             pw.println("CPU_abis    : ${abis}")
61         }else{
62             pw.println("CPU_abi     : ${Build.CPU_ABI}")
63         }
64     }
65 }

如图:

android 捕获未try的异常、抓取崩溃日志

2.2 在application里注册

1     lateinit var crash      :   CrashHandler
2     fun crashHandler(){
3         crash = CrashHandler(context = applicationContext)
4         crash.start()
5     }

2.3 在自定义的线程中注册

 1  private void init(){
 2 
 3         new Thread(){
 4             @Override
 5             public void run() {
 6                 setUncaughtExceptionHandler(crashHandler);
 7                 int num = 100 / 0;
 8             }
 9         }.start();
10     }
11     public CrashHandler crashHandler = new CrashHandler();
12     public class CrashHandler implements Thread.UncaughtExceptionHandler {
13         final String TAG = "CrashHandler";
14 
15         @Override
16         public void uncaughtException(Thread t, Throwable e) {
17             e.printStackTrace();
18             Log.e(TAG, "uncaughtException: " + e.getMessage() + " thread = " + t.getId());
19             if (t.getId() == 1){
20                 //...
21             }
22             //异常信息收集
23             //应用程序信息收集
24             //保存错误报告文件到文件。
25         }
26     }