Android - CrashHandler 全局异常捕获器

news2024/12/24 9:33:59

官网介绍如下: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

记录如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1368762.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

unity:玩家从SDK注册到登陆就流失百分10几的思考

目录 前言 失去焦点问题&#xff08;黑屏&#xff09; 缓存cache.ready没有准备好&#xff08;黑屏&#xff09; 兼容性问题&#xff08;黑屏和闪退&#xff09; 用户隐私协议弹出时机&#xff08;体验&#xff09; 启动画面和登陆界面&#xff08;体验&#xff09; 游戏…

2024电影营销:转换为王,平台变阵

2023年&#xff0c;电影市场全面复苏&#xff0c;伴随着各类大片纷纷入场掘金&#xff0c;“电影营销”再度成为了今年热议的话题。 关于张艺谋拍一部戏到底要被震撼多少次&#xff1f;吴京为什么半夜老是喜欢溜达在别的剧组探班&#xff1f;徐峥一年躲在观众席里&#xff0c;…

亚信安慧AntDB数据库荣膺“2023中国数据库领域最具商业合作价值企业”殊荣

近期&#xff0c;引领数据库技术创新的亚信科技荣获数据猿发布的《2023中国数据库领域最有商业合作潜力企业排名》中的重要位置。此次入选&#xff0c;不仅彰显了亚信科技在数据库领域近二十年来的技术实力和行业认可&#xff0c;也凸显了其在商业合作潜力方面的引领地位。 图1…

Mac 安装Nginx教程

Nginx官网 Nginx官网英文 1.在终端输入brew search nginx 命令检查nginx是否安装了 2. 安装命令&#xff1a;brew install nginx 3. 查看Nginx信息命令brew info nginx 4. 启动 nginx方式&#xff1a;在终端里输入 nginx 5.查看 nginx 是否启动成功 在浏览器中访问http://l…

通达信顾比均线指标公式,识别价格趋势的变化和潜在突破

顾比均线(GMMA)是由全球知名的技术分析大师戴若顾比(Daryl Guppy)发明的的技术指标&#xff0c;通过观察短期和长期移动平均线之间的关系来识别价格趋势的变化和潜在突破。 顾比均线使用了两组移动平均线&#xff0c;每组包含6条&#xff0c;总共12条。短期均线组通常包括 3 日…

人工智能图像生成的道德利弊

目录 一、我们应该关注人工智能图像吗&#xff1f;二、利用人工智能增强创造力的积极作用三、版权和剽窃问题四、对就业和劳动力动态的影响五、无意识的偏见和影响六、负责任地前行 人工智能&#xff08;AI&#xff09;发展迅速&#xff0c;尤其是近年来。据估计&#xff0c;超…

【办公】百度网盘 Linux命令行方式使用

安装&#xff1a; pip install bypy登录&#xff1a; bypy info # 会给网页链接&#xff0c;用浏览器打开链接后会有一个授权码&#xff0c;复制授权码后粘贴到这里回车上传文件&#xff1a; bypy upload ./data下载文件&#xff1a; bypy downdir /运行时添加-v参数&#…

以数据资产入表为抓手,推动数据资产化

在数字化时代&#xff0c;数据已经成为企业的重要资产。数据资产化是将数据视为一种有价值的资产&#xff0c;对其进行有效管理和利用的过程。而数据资产入表则是将数据资产纳入财务报表&#xff0c;以反映其价值和对企业财务状况的影响。本文亿信华辰 将深入探讨数据资产化与数…

MIT_线性代数笔记:第 25 讲 对称矩阵和正定性

目录 对称矩阵 Symmetric matrices实特征值 Real eigenvalues正定矩阵 Positive definite matrices 对称矩阵是最重要的矩阵之一&#xff0c;其特征值为实数并且拥有一套正交特征向量。正定矩阵的性质则更好。 对称矩阵 Symmetric matrices 包含特殊性质的矩阵&#xff0c;例如…

Freertos:

裸机编程通过中断实现不同任务的切换&#xff0c;实际上RTOS中通过不断更换CPU的使用权达到多任务运行的目的。FreeRTOS 中任务存在四种任务状态&#xff0c;分别为运行态、就绪态、阻塞态和挂起态。任务一般通过函数 vTaskSuspend()和函数 vTaskResums()进入和退出挂起态&…

2024.1.8 关于 Redis 数据类型 Zset 集合命令、编码方式、应用场景

目录 引言 Zset 集合命令 ZINTERSTORE ZUNIONSTORE Zset 编码方式 Zset 应用场景 排行榜系统 引言 在 Redis 中集合间操作无非就是 交集、并集、差集 Set 类型与之相对应的操作命令为 sinter、sunion、sdiff 注意&#xff1a; 从 Redis 6.2 版本开始&#xff0c;Zset 命…

书生·浦语大模型实战2

轻松玩转书生浦语大模型趣味 Demo 大模型及 InternLM 模型简介 什么是大模型 大模型通常指的是机器学习或人工智能领域中参数数量巨大、拥有庞大计算能力和参数规模的模型。这些模型利用大量数据进行训练&#xff0c;并且拥有数十亿甚至数千亿个参数。大模型的出现和发展得益…

IP定位应对恶意IP攻击:保护网络安全的新策略

随着网络攻击的日益猖獗&#xff0c;恶意IP攻击成为网络安全领域的一大挑战。传统的安全防护手段在应对此类攻击时显得力不从心。近年来&#xff0c;通过IP定位这一新技术&#xff0c;为应对恶意IP攻击提供了新的解决思路。 IP定位技术通过分析网络流量中的IP地址&#xff0c;能…

YOLOv8改进 | 主干篇 | 12月份最新成果TransNeXt特征提取网络(全网首发)

一、本文介绍 本文给大家带来的改进机制是TransNeXt特征提取网络,其发表于2023年的12月份是一个最新最前沿的网络模型,将其应用在我们的特征提取网络来提取特征,同时本文给大家解决其自带的一个报错,通过结合聚合的像素聚焦注意力和卷积GLU,模拟生物视觉系统,特别是对于中…

003集Class类应用实例—python基础入门实例

面向对象编程是一种编程方式&#xff0c;此编程方式的落地需要使用 “类” 和 “对象” 来实现&#xff0c;所以&#xff0c;面向对象编程其实就是对 “类” 和 “对象” 的使用。 类就是一个模板&#xff0c;模板里可以包含多个函数&#xff0c;函数里实现一些功能 对象则是根…

Uncaught (in promise) ReferenceError: require is not defined

在 Vue3 中加载项目路径下的资源图片,起初按照之前 vue 的写法 require 但浏览器却抛出了异常 Uncaught (in promise) ReferenceError: require is not defined 因为 require 采用的 webpack 加载方式,而 vue3 中通过 vite 的方式,两者存在差异,所以才产生了刚开始的一目; vu…

无线信号强度测试板/射频产品量产测试神器

目录 一、测试板特点 二、应用场景 三、芯片特点 四、测试板接口图 …

linux查找文件中的指定字符,并批量替换文件中指定字符

1、grep -rl “jquery-1.8.0.min.js” /opt 查找/opt 目录下存在jquery-1.8.0.min.js的文件 [rootlocalhost file1]# grep -rl "jquery-1.8.0.min.js" /opt /opt/file1/index.xhtml /opt/file2/index.xhtml /opt/shell.sh扩展&#xff1a;使用该命令可以查找文件中…

多链混沌:Layer2 格局演变与跨链流动性的新探索

点击查看原文&#xff1a;多链混沌&#xff1a;Layer2 格局演变与跨链流动性的新探索 如今的 Crypto 是一个由多链构成的混沌世界。曾经&#xff0c;以太坊聚集了加密世界绝大多数的流动性与 DeFi 应用&#xff0c;但现在其 TVL 占比已经降到 60% 以下&#xff0c;并仍处于下降…

Unity文字转语音(使用RT-Voice PRO [2023.1.0])

参考文章Unity插件——文字转朗读语音RtVioce插件功能/用法/下载_rtvoice-CSDN博客 一、使用步骤 1.导入进Unity&#xff08;插件形式为 .unitypackage&#xff09; https://download.csdn.net/download/luckydog1120446388/88717512 2.添加所需Prefab 1&#xff09;.右键可…