官网介绍如下:Thread.UncaughtExceptionHandler (Java Platform SE 8 )
用于线程因未捕获异常而突然终止时调用的处理程序接口。当线程由于未捕获异常而即将终止时,Java虚拟机将使用thread . getuncaughtexceptionhandler()查询该线程的UncaughtExceptionHandler,并调用该处理程序的uncaughtException方法,将线程和异常作为参数传递。
如果一个线程没有显式设置它的UncaughtExceptionHandler,那么它的ThreadGroup对象充当它的UncaughtExceptionHandler。如果ThreadGroup对象对处理异常没有特殊要求,它可以将调用转发给默认的未捕获异常处理程序。
基于此点,我们可以将其应用为整个app出现异常后,没有在此处添加异常处理的时候,捕获异常并进行通用处理。
1、首先实现 Thread.UncaughtExceptionHandler
接口并创建一个单例
class CrashHandler : Thread.UncaughtExceptionHandler {
companion object {
private const val TAG = "CrashHandler"
//外部存储目录
private val INNER_PATH = Environment.getExternalStorageDirectory().path
//自定义文件夹
private const val SELF_FILE = "/lichang/"
//文件名称
private const val CRASH_FILE = "crash_error.log"
@SuppressLint("SimpleDateFormat")
private val formatter: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
val sInstance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
CrashHandler()
}
}
...
2、添加初始化方法
private var infos = mutableMapOf<String, String>()
private var mContext: Context? = null
private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null
//初始化
fun init(context: Context) {
this.mContext = context
this.mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler(this)
}
3、重写 uncaughtException()
方法
override fun uncaughtException(thread: Thread, throwable: Throwable) {
if ((!handleException(throwable)) && (this.mDefaultHandler != null)) {
//如果代码中没有处理交给系统处理
this.mDefaultHandler!!.uncaughtException(thread, throwable);
return;
}
//延时3秒杀死进程
Log.e(TAG, "Process will be killed in 2 seconds!");
try {
Thread.sleep(2000)
} catch (e: InterruptedException) {
Log.e(TAG, "error : ", e)
}
android.os.Process.killProcess(android.os.Process.myPid())
exitProcess(1)
}
/**
* 处理异常信息
* @param throwable
* @return
*/
private fun handleException(throwable: Throwable?): Boolean {
if (throwable == null) {
return false
}
object : Thread() {
override fun run() {
Looper.prepare()
//在此处处理出现异常的情况
throwable.printStackTrace()
Log.e(TAG, "出现了未捕获异常!!!")
Looper.loop()
}
}.start()
this.mContext?.let { collectDeviceInfo(it) }
saveCrashInfo2File(throwable)
return true
}
/**
* 收集设备信息
* @param context
*/
private fun collectDeviceInfo(context: Context) {
val packageInfo: PackageInfo?
try {
packageInfo = context.packageManager.getPackageInfo(
context.packageName,
PackageManager.GET_ACTIVITIES
)
if (packageInfo != null) {
val versionName =
if (packageInfo.versionName == null) "null" else packageInfo.versionName
val versionCode = packageInfo.versionCode.toString() + ""
this.infos["versionName"] = versionName
this.infos["versionCode"] = versionCode
}
} catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "an error occured when collect package info", e)
}
//获取Build信息
val declaredFields: Array<Field> = Build::class.java.declaredFields
val length = declaredFields.size
var i = 0
while (i < length) {
val declaredField: Field = declaredFields[i]
try {
declaredField.setAccessible(true)
this.infos[declaredField.getName()] = declaredField.get(null).toString()
} catch (e: Exception) {
Log.e(TAG, "an error occured when collect crash info", e)
}
i += 1
}
}
/**
* 保存crash信息到文件
* @param throwable
* @return
*/
private fun saveCrashInfo2File(throwable: Throwable): String? {
var throwable: Throwable? = throwable
val crashInfos = StringBuffer()
//添加分隔行
crashInfos.append("\r\n")
//添加crash时间
crashInfos.append("Crash Time")
crashInfos.append("=")
crashInfos.append(formatter.format(Date(System.currentTimeMillis())))
crashInfos.append("\r\n")
val iterator: Iterator<Map.Entry<String, String>> = infos.entries.iterator()
var next: Map.Entry<String, String>? = null
while (iterator.hasNext()) {
next = iterator.next()
crashInfos.append(next.key)
crashInfos.append("=")
crashInfos.append(next.value)
crashInfos.append("\r\n")
}
val exceptionInfo = StringWriter()
val printWriter = PrintWriter(exceptionInfo)
throwable!!.printStackTrace(printWriter)
//循环获取throwable的栈信息
throwable = throwable.cause
while (throwable != null) {
throwable.printStackTrace(printWriter)
throwable = throwable.cause
}
printWriter.close()
crashInfos.append(exceptionInfo.toString())
try {
System.currentTimeMillis()
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
val file = File("$INNER_PATH$SELF_FILE")
if (!file.exists()) {
file.mkdirs()
}
val fos = FileOutputStream("$INNER_PATH$SELF_FILE$CRASH_FILE", true)
fos.write(crashInfos.toString().toByteArray())
fos.close()
}
return CRASH_FILE
} catch (e: Exception) {
Log.e(TAG, "an error occured while writing file...", e)
}
return null
}
4、在 BaseApplication 里实现,activity 里也行,但毕竟 application 生命周期更长,可以更好的捕获异常位置。
CrashHandler.sInstance.init(this)
5、验证手动抛出一个error,“ throw Exception("error")”
logcat 输出内容如下:
10:48:38.287 AndroidRuntime D Shutting down VM
10:48:38.293 AndroidRuntime E FATAL EXCEPTION: main
Process: com.lichang.source, PID: 29787
java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:563)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:553)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.Exception: error
at com.lichang.source.MainActivity.onCreate$lambda$0(MainActivity.kt:23)
at com.lichang.source.MainActivity.$r8$lambda$1d06GL5SsQcwKha8s7cR5MexWzU(Unknown Source:0)
at com.lichang.source.MainActivity$$ExternalSyntheticLambda0.onClick(Unknown Source:0)
at android.view.View.performClick(View.java:7448)
at android.view.View.performClickInternal(View.java:7421)
at android.view.View.access$3700(View.java:838)
at android.view.View$PerformClick.run(View.java:28870)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7941)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:553)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
10:48:38.299 System.err W java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
10:48:38.299 System.err W at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:563)
10:48:38.299 System.err W at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
10:48:38.300 System.err W Caused by: java.lang.reflect.InvocationTargetException
10:48:38.300 System.err W at java.lang.reflect.Method.invoke(Native Method)
10:48:38.300 System.err W at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:553)
10:48:38.300 System.err W ... 1 more
10:48:38.300 System.err W Caused by: java.lang.Exception: error
10:48:38.301 System.err W at com.lichang.source.MainActivity.onCreate$lambda$0(MainActivity.kt:23)
10:48:38.301 System.err W at com.lichang.source.MainActivity.$r8$lambda$1d06GL5SsQcwKha8s7cR5MexWzU(Unknown Source:0)
10:48:38.301 System.err W at com.lichang.source.MainActivity$$ExternalSyntheticLambda0.onClick(Unknown Source:0)
10:48:38.301 System.err W at android.view.View.performClick(View.java:7448)
10:48:38.302 System.err W at android.view.View.performClickInternal(View.java:7421)
10:48:38.302 System.err W at android.view.View.access$3700(View.java:838)
10:48:38.302 System.err W at android.view.View$PerformClick.run(View.java:28870)
10:48:38.302 System.err W at android.os.Handler.handleCallback(Handler.java:938)
10:48:38.302 System.err W at android.os.Handler.dispatchMessage(Handler.java:99)
10:48:38.303 System.err W at android.os.Looper.loopOnce(Looper.java:201)
10:48:38.303 System.err W at android.os.Looper.loop(Looper.java:288)
10:48:38.303 System.err W at android.app.ActivityThread.main(ActivityThread.java:7941)
10:48:38.303 System.err W ... 3 more
10:48:38.303 CrashHandler E 出现了未捕获异常!!!
10:48:38.472 CrashHandler E Process will be killed in 2 seconds!
---------------------------- PROCESS ENDED (29787) for package com.lichang.source ----------------------------
10:48:40.473 Process I Sending signal. PID: 29787 SIG: 9
记录如下: