Android - CrashHandler 全局异常捕获器

官网介绍如下:Thread.UncaughtExceptionHandler (Java Platform SE 8 )

用于线程因未捕获异常而突然终止时调用的处理程序接口。当线程由于未捕获异常而即将终止时,Java虚拟机将使用thread . getuncaughtexceptionhandler()查询该线程的UncaughtExceptionHandler,并调用该处理程序的uncaughtException方法,将线程和异常作为参数传递。

如果一个线程没有显式设置它的UncaughtExceptionHandler,那么它的ThreadGroup对象充当它的UncaughtExceptionHandler。如果ThreadGroup对象对处理异常没有特殊要求,它可以将调用转发给默认的未捕获异常处理程序。

基于此点,我们可以将其应用为整个app出现异常后,没有在此处添加异常处理的时候,捕获异常并进行通用处理。

1、首先实现 Thread.UncaughtExceptionHandler 接口并创建一个单例
Kotlin 复制代码
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、添加初始化方法
Kotlin 复制代码
    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() 方法
Kotlin 复制代码
    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 生命周期更长,可以更好的捕获异常位置。
Kotlin 复制代码
CrashHandler.sInstance.init(this)
5、验证手动抛出一个error," throw Exception("error")"

logcat 输出内容如下:

Kotlin 复制代码
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

记录如下:

相关推荐
松仔log6 小时前
Java中级——组合和继承
android·java·开发语言
我命由我123459 小时前
Jetpack Compose - MaterialExpressiveTheme 与 MaterialTheme、ColorScheme
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime
lilian23310 小时前
Harmony os 技术实战|拼豆制图06:收藏 ID、生成记录与重启恢复怎么不打架
android·java·数据库·harmonyos
2601_9557596210 小时前
ClaudeAPI成本中心与业务标签设计指南
android·java·数据库
余衫马10 小时前
2026最新版!Android Studio 极速安装与配置指南
android·ide·android studio
AFinalStone10 小时前
Android 7系统异常问题排查(五)Framework层(下)—System Server崩溃
android·tombstone·系统异常
AFinalStone10 小时前
Android 7系统异常问题排查(八)系统追踪—Trace机制与性能诊断
android·系统异常
极客猴子14 小时前
Android录音转写频繁卡顿?多款APP长时间会议场景稳定性实测
android·人工智能·智能手机·飞书
Kapaseker14 小时前
Boolean 变量到底该怎么命名?
android·kotlin
AFinalStone14 小时前
Android 7系统异常问题排查(六)应用异常(上)—ANR机制全解
android·系统异常