Android app Java层异常捕获方案

背景: 在Android app运行中,有时一些无关紧要的异常出现时希望App 不崩溃,能继续让用户操作,可以有效提升用户体验和增加业务价值。

在这里插入图片描述

在这里插入图片描述
新流程
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
哪些场景需要Catch

这里是引用

Crash Config配置信息:
在这里插入图片描述

支持从网络上获取Crash配置表,动态防护,避免crash。

使用: 在Application onCreate中调用:

CrashPortrayHelper.INSTANCE.init(this);

实现原理—源代码:

CrashPortray.kt

package com.mcd.library.crashProtectimport com.google.gson.annotations.SerializedName
import java.io.Serializabledata class CrashPortray(@SerializedName("class_name")val className: String = "",val message: String = "",val stack: List<String> = emptyList(),@SerializedName("app_version")val appVersion: List<String> = emptyList(),@SerializedName("os_version")val osVersion: List<Int> = emptyList(),val model: List<String> = emptyList(),val type: String = "all",@SerializedName("clear_cache")val clearCache: Int = 0,@SerializedName("finish_page")val finishPage: Int = 0,val toast: String = ""
) : Serializable {fun valid(): Boolean {return className.isNotEmpty() || message.isNotEmpty() || stack.isNotEmpty()}
}

CrashPortrayHelper.kt

package com.mcd.library.crashProtectimport android.app.Application
import android.content.Context
import android.os.Build
import com.mcd.library.AppConfigLib
import com.mcd.library.common.McdLifecycleCallback
import com.mcd.library.utils.CacheUtil
import com.mcd.library.utils.DialogUtil
import java.io.File
import java.lang.reflect.InvocationTargetExceptionobject CrashPortrayHelper {private var crashPortrayConfig: List<CrashPortray>? = nullprivate lateinit var application: Applicationprivate lateinit var actionImpl: IAppprivate const val crashProtectClosed: Boolean = false // 是否关闭该功能fun init(application: Application) {if (AppConfigLib.isDebugMode() || crashProtectClosed) { // debug模式下不进行初始化return}CrashPortrayHelper.application = applicationcrashPortrayConfig = getCrashConfig()actionImpl = getAppImpl()CrashUncaughtExceptionHandler.init()}private fun getCrashConfig(): List<CrashPortray> {// 从网络获取crash配置val crashList = AppConfigLib.getCrashPortrays() ?: ArrayList()//添加本地默认配置crashList.apply {this.addAll(getSystemException())this.addAll(getRNException())this.addAll(getSDKException())}return crashList}// 三方sdk异常private fun getSDKException(): List<CrashPortray> {val crashList = mutableListOf<CrashPortray>()crashList.add(CrashPortray(className = "IllegalArgumentException",message = "[PaymentActivity] not attached to window manager")) // 支付页crashList.add(CrashPortray(className = "EOFException")) //lottiecrashList.add(CrashPortray(className = "JsonEncodingException")) //lottiereturn crashList}// 系统异常private fun getSystemException(): List<CrashPortray> {val crashList = mutableListOf<CrashPortray>()crashList.add(CrashPortray(className = "BadTokenException"))crashList.add(CrashPortray(className = "AssertionError"))crashList.add(CrashPortray(className = "NoSuchMethodError"))crashList.add(CrashPortray(className = "NoClassDefFoundError"))crashList.add(CrashPortray(className = "CannotDeliverBroadcastException"))crashList.add(CrashPortray(className = "OutOfMemoryError"))crashList.add(CrashPortray(className = "DeadSystemRuntimeException"))crashList.add(CrashPortray(className = "DeadSystemException"))crashList.add(CrashPortray(className = "NullPointerException"))crashList.add(CrashPortray(className = "TimeoutException"))crashList.add(CrashPortray(className = "RemoteException"))crashList.add(CrashPortray(className = "SecurityException"))crashList.add(CrashPortray(className = "TransactionTooLargeException"))crashList.add(CrashPortray(className = "SQLiteFullException"))crashList.add(CrashPortray(className = "ConcurrentModificationException"))crashList.add(CrashPortray(className = "InvocationTargetException"))return crashList}// RN异常private fun getRNException(): List<CrashPortray> {val crashList = mutableListOf<CrashPortray>()crashList.add(CrashPortray(className = "TooManyRequestsException"))crashList.add(CrashPortray(className = "RuntimeException",message = "Attempting to call JS function on a bad application bundle"))crashList.add(CrashPortray(className = "RuntimeException",message = "Illegal callback invocation from native module"))crashList.add(CrashPortray(className = "CppException",message = "facebook::react::Recoverable"))crashList.add(CrashPortray(className = "JavascriptException"))crashList.add(CrashPortray(className = "UnsupportedOperationException",message = "Tried to obtain display from a Context not associated with one"))crashList.add(CrashPortray(className = "MissingWebViewPackageException"))return crashList}private fun getAppImpl(): IApp {return object : IApp {override fun showToast(context: Context, msg: String) {DialogUtil.showShortPromptToast(context, msg)}override fun cleanCache(context: Context) {CacheUtil.trimCache(context.applicationContext)}override fun finishCurrentPage() {McdLifecycleCallback.getInstance().finishActivityWithNumber(1)}override fun getVersionName(context: Context): String =AppConfigLib.getCurrentVersionName()override fun downloadFile(url: String): File? {return null}override fun readStringFromCache(key: String): String {return ""}override fun writeStringToCache(file: File, content: String) {}}}fun needProtect(throwable: Throwable): Boolean {val config: List<CrashPortray>? = crashPortrayConfigif (config.isNullOrEmpty()) {return false}kotlin.runCatching {for (i in config.indices) {val crashPortray = config[i]if (!crashPortray.valid()) {continue}//1. app 版本号if (crashPortray.appVersion.isNotEmpty()&& !crashPortray.appVersion.contains(actionImpl.getVersionName(application))) {continue}//2. os_versionif (crashPortray.osVersion.isNotEmpty()&& !crashPortray.osVersion.contains(Build.VERSION.SDK_INT)) {continue}//3. modelif (crashPortray.model.isNotEmpty()&& crashPortray.model.firstOrNull { Build.MODEL.equals(it, true) } == null) {continue}var throwableName = throwable.javaClass.simpleNameval message = throwable.message ?: ""if (throwable.cause is InvocationTargetException) { // 处理原始异常(华为等机型)throwableName = (throwable.cause as InvocationTargetException).targetException.javaClass.simpleName ?: ""}//4. class_nameif (crashPortray.className.isNotEmpty()&& crashPortray.className != throwableName) {continue}//5. messageif (crashPortray.message.isNotEmpty() && !message.contains(crashPortray.message)) {continue}//6. stackif (crashPortray.stack.isNotEmpty()) {var match = falsethrowable.stackTrace.forEach { element ->val str = element.toString()if (crashPortray.stack.find { str.contains(it) } != null) {match = truereturn@forEach}}if (!match) {continue}}//7. 相应操作if (crashPortray.clearCache == 1) {actionImpl.cleanCache(application)}if (crashPortray.finishPage == 1) {actionImpl.finishCurrentPage()}if (crashPortray.toast.isNotEmpty()) {actionImpl.showToast(application, crashPortray.toast)}return true}}return false}
}

CrashUncaughtExceptionHandler.kt

package com.mcd.library.crashProtectimport android.os.Looper
import com.mcd.appcatch.AppInfoOperateProvider
import com.mcd.appcatch.appEvent.AppEventName
import com.mcd.library.utils.JsonUtilobject CrashUncaughtExceptionHandler : Thread.UncaughtExceptionHandler {private var oldHandler: Thread.UncaughtExceptionHandler? = nullfun init() {oldHandler = Thread.getDefaultUncaughtExceptionHandler()oldHandler?.let {Thread.setDefaultUncaughtExceptionHandler(this)}}override fun uncaughtException(t: Thread, e: Throwable) {if (CrashPortrayHelper.needProtect(e)) {report(e)bandage()return}//旧的处理方式oldHandler?.uncaughtException(t, e)}// crash 信息上报private fun report(e: Throwable) {kotlin.runCatching {AppInfoOperateProvider.getInstance().saveEventInfo(AppEventName.Crash.crash_protect_report,System.currentTimeMillis(), e.message + JsonUtil.encode(e.stackTrace.take(5))) // 取message+异常堆栈前5条}}/*** 让主线程恢复运行*/private fun bandage() {try {if (Looper.myLooper() != Looper.getMainLooper()) {return}Looper.loop()} catch (e: Exception) {uncaughtException(Thread.currentThread(), e)}}
}

IApp.kt

package com.mcd.library.crashProtectimport android.content.Context
import java.io.Fileinterface IApp {fun showToast(context: Context, msg: String)fun cleanCache(context: Context)fun finishCurrentPage()fun getVersionName(context: Context): Stringfun downloadFile(url: String): File?fun readStringFromCache(key : String): Stringfun writeStringToCache(file: File, content: String)
}

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

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

相关文章

PPT录屏怎么录?PPT录屏,3种方法简单操作

在数字化时代&#xff0c;PPT已经成为我们日常工作、学习和生活中不可或缺的一部分。无论是商务报告、教学课件还是产品展示&#xff0c;PPT都能帮助我们更加生动、直观地传递信息。然而&#xff0c;有时候我们会面临PPT录屏怎么录的问题。这时&#xff0c;一个好的PPT录屏功能…

合同与合规管理:国企数字化转型之路

在全球经济一体化的背景下&#xff0c;国有企业作为国家经济的重要支撑&#xff0c;其稳健的操作和高效的管理备受瞩目。随着市场经济条件的不断演变和法规的日益严格&#xff0c;传统的手动处理合同和合规管理方式已逐步显示出局限性。采纳先进的合同管理系统和合规管理系统从…

【C++11(二)】lambda表达式和可变参数模板

一、可变参数模板 C11的新特性可变参数模板 能够让您创建可以接受 可变参数的函数模板和类模板 // Args是一个模板参数包&#xff0c;args是一个函数形参参数包 // 声明一个参数包Args...args&#xff0c;这个参数包中可以包含0到任意个模板参数。 template <class ...Arg…

「树莓派入门」树莓派基础-无显示屏连接与远程连接

一、无显示屏树莓派的硬件和软件准备 硬件需求 树莓派主板&#xff08;任何型号&#xff09;电源适配器至少8GB容量的SD卡&#xff08;推荐Class 10或UHS-I&#xff09;以太网线或Wi-Fi网络环境 软件需求 操作系统镜像&#xff08;如Raspberry Pi OS&#xff09;烧录工具&a…

SaaS应用程序开发流程【完整指南】

SaaS 市场每天都在经历巨大的增长。随着 SaaS 业务模式在企业中越来越受欢迎&#xff0c;对优秀 SaaS 应用开发公司的需求也日益增加。 2021年全球SAAS市场净值为1437.7亿美元。统计数据显示&#xff0c; 2022年至2028年&#xff0c;市场将继续以25.89%的复合年增长率增长&…

Python XPath超详细教程

1.什么是Xpath 1.概念:XPath(XML Path Language)&#xff1a;XML路径语言&#xff0c;用来确定XML文档中某部分位置的语言 2.如何使用首先我们需要去Python库里进行下载在终端(按住widr然后会有一个输入框在里面输入cmd就会进入终端)然后输入下面的代码就好了 pip install lx…

科普文:贝叶斯过滤器判定垃圾邮件

简介 贝叶斯分类的运作是借着使用标记(一般是字词&#xff0c;有时候是其他)与垃圾邮件、非垃圾邮件的关连&#xff0c;然后搭配贝叶斯推断来计算一封邮件为垃圾邮件的可能性。 贝叶斯垃圾邮件过滤是非常有威力的技术&#xff0c;可以修改自己以符合个别使用者的需要&#xff0…

C# Onnx Yolov8-OBB 旋转目标检测 行驶证副页条码+编号 检测,后续裁剪出图片并摆正显示

C# Onnx Yolov8-OBB 旋转目标检测 行驶证副页条码编号 检测&#xff0c;后续裁剪出图片并摆正显示 目录 效果 模型信息 项目 代码 下载 效果 模型信息 Model Properties ------------------------- date&#xff1a;2024-06-25T10:59:15.206586 description&#xff1a;…

JavaScript Array(数组)

JavaScript Array(数组) JavaScript 中的 Array(数组)是一种用于存储数据的集合,它允许我们存储多个数据项,并可以通过索引来访问这些数据项。数组是 JavaScript 中非常基础且强大的数据结构,广泛应用于各种编程场景。 数组的创建 在 JavaScript 中,创建数组的方式有…

讯飞星火通过API接入

国内的GPT王者讯飞星火 如果你想暴富 可以关注一下科大讯飞 看看它的股票走势 每次讯飞星火发布新版本的时候 都掀起了一个小涨停 科大讯飞作为国内领先的人工智能企业 常年以来积累的优势 包括智能客服、语音识别、机器翻译等 多场景的大量应用 铺垫了海量的训练数据…

【分布式】Pytorch在多GPU环境的分布式训练中常见问题汇总

文章目录 第一部分:分布式训练的基本概念1. 分布式训练简介2. 关键概念第二部分:环境设置1. 安装和配置2. 初始化进程组第三部分:模型构建与封装1. 构建模型2. 注意事项第四部分:数据处理1. DataLoader2. 数据分割第五部分:训练过程第六部分:调试和优化1. 调试技巧2. 性能…

PostgreSQL 安全性与权限管理(八)

1. 用户和角色管理 1.1 创建角色 在 PostgreSQL 中&#xff0c;角色可以是用户或组的抽象概念&#xff0c;用于管理数据库的访问权限。 1.1.1 创建角色 CREATE ROLE role_name; 1.1.2 赋予角色权限 GRANT permission_type ON object TO role_name; 1.2 用户管理 用户是…

React 19 新特性集合

前言&#xff1a;https://juejin.cn/post/7337207433868197915 新 React 版本信息 伴随 React v19 Beta 的发布&#xff0c;React v18.3 也一并发布。 React v18.3相比最后一个 React v18 的版本 v18.2 &#xff0c;v18.3 添加了一些警告提示&#xff0c;便于尽早发现问题&a…

PostgreSQL 分区表与并行查询(十)

1. 分区表概述 1.1 什么是分区表 分区表是将大表分割成更小、更可管理的部分的技术。每个分区表都可以单独进行索引和查询&#xff0c;从而提高查询性能和管理效率。 1.2 分区策略 1.2.1 基于范围的分区 按照时间范围或者数值范围进行分区&#xff0c;如按月或按地区。 C…

利用百数应用优化制造细节,提升生产效率的技术实践

制造管理是确保企业高效、高质生产的核心环节&#xff0c;对于提高企业的运营效率、质量控制、成本控制、交货期保障、资源优化、创新能力以及风险管理等方面都具有重要意义&#xff0c;它能帮助企业在激烈的市场竞争中保持领先地位&#xff0c;同时实现资源的有效利用和风险的…

CSS|02 基本选择器

选择器 什么是选择器 选择器是指通过一定的语法规则选取到对应的HTML标记&#xff0c;然后给这个对应的HTML标记设置样式。 选择器分为四大类&#xff1a;基本选择器、复合选择器、 伪类选择器、属性选择器基本选择器通用选择器&#xff1a;将匹配HTML所有标签。不建议使用。…

顺序栈与链式栈

目录 1. 栈 1.1 栈的概念 2. 栈的实现 3. 顺序栈的实现 3.1 顺序栈的声明 3.2 顺序栈的初始化 3.3 顺序栈的入栈 3.4 顺序栈的出栈 3.5 顺序栈获取栈顶元素 3.6 顺序栈获取栈内有效数据个数 3.7 顺序栈判断栈是否为空 3.8 顺序栈打印栈内元素 3.9 顺序栈销毁栈 3…

[数据集][目标检测]鸡蛋缺陷检测数据集VOC+YOLO格式2918张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2918 标注数量(xml文件个数)&#xff1a;2918 标注数量(txt文件个数)&#xff1a;2918 标注…

什么是大语言模型

前言 自从去年chatgpt横空出世以来&#xff0c;它火爆也让大语言模型这个词变的很流行&#xff0c;到底什么是大语言模型&#xff0c;今天从初学者的角度介绍一下大语言模型的基本概念、组成部分和基本工作流程等。下面的介绍中如果涉及到一些专业术语不太理解&#xff0c;也没…

携程暑期实习一面

携程暑期实习一面 4.7 50min 微核 两段实习时间节点&#xff0c;为什么想这么早去实习。讲一下测试工作的流程是什么样子的对于测试用例方法设计上你了解哪些讲一下你所提到的等价类划分法和边界值分析法的具体概念&#xff0c;还了解哪些方法一个场景&#xff0c;web端或者…