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 }
如图:
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 }