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…

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

简介 贝叶斯分类的运作是借着使用标记(一般是字词&#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;…

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…

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

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

顺序栈与链式栈

目录 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 标注…

算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 决策树是一种简单直观的机器学习算法&#xff0c;它广泛应用于分类和回归问题中。它的核心思想是将复杂的决策过程分解成一系列简单的决…

【推荐】Prometheus+Grafana企业级监控预警实战

新鲜出炉&#xff01;&#xff01;&#xff01;PrometheusGrafanaAlertmanager springboot 企业级监控预警实战课程&#xff0c;从0到1快速搭建企业监控预警平台&#xff0c;实现接口调用量统计&#xff0c;接口请求耗时统计…… 详情请戳 https://edu.csdn.net/course/detai…

Word页码设置,封面无页码,目录摘要阿拉伯数字I,II,III页码,正文开始123为页码

一、背景 使用Word写项目书或论文时&#xff0c;需要正确插入页码&#xff0c;比如封面无页码&#xff0c;目录摘要阿拉伯数字I&#xff0c;II&#xff0c;III为页码&#xff0c;正文开始以123为页码&#xff0c;下面介绍具体实施方法。 所用Word版本&#xff1a;2021 二、W…

HTTPS 代理的优点和缺点是什么?

HTTPS&#xff08;超文本安全传输协议&#xff09;作为一种基于HTTP加上SSL安全层的网络通信协议&#xff0c;已经成为互联网上广泛使用的IP协议之一。它在保证信息安全和隐私方面具有很多优势&#xff0c;但也存在一些缺点。接下来&#xff0c;我们就来探究一下HTTPS协议的优缺…

Qt篇——获取Windows系统上插入的串口设备的物理序号

先右键【此电脑-管理- 设备管理器-端口&#xff08;COM和LPT&#xff09;】中找到我们插入的某个设备的物理序号&#xff0c;如下图红色矩形框出的信息&#xff0c;这个就是已插入设备的物理序号&#xff08;就是插在哪个USB口的意思&#xff09;。 在Linux下我们可以通过往/et…

【踩坑】修复循环设置os.environ[‘CUDA_VISIBLE_DEVICES‘]无效

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 问题示例 for gpus in [0, 1, 2, 3, 4, 5, 6, 7]:os.environ[CUDA_VISIBLE_DEVICES] gpusprint(torch.cuda.get_device_name(0)) 始终将使用第…

Mac安装多版本node

Mac下使用n模块去安装多个指定版本的Node.js&#xff0c;并使用命令随时切换。 node中的n模块是&#xff0c;node专门用来管理node版本的模块&#xff0c;可以进行node版本的切换&#xff0c;下载&#xff0c;安装。 1.安装n npm install -g n 2.查看版本 n --version 3.展…

动作捕捉与数字人实训室,引领动漫专业创新发展

如今&#xff0c;随着全身动作捕捉设备在动漫行业中的应用越来越重要&#xff0c;传统的教学模式与市场需求逐渐脱节&#xff0c;原有的教学方式和思路急需进行调整。高校通过搭建动作捕捉与数字人实训室&#xff0c;可以使得教学质量和效率大大提升&#xff0c;让学生能够接触…

如何采集拼多多的商品或店铺数据

怎么使用简数采集器批量采集拼多多的商品或店铺相关信息呢&#xff1f; 简数采集器暂时不支持采集拼多多的商品或店铺相关数据&#xff0c;只能采集页面公开显示的信息&#xff0c;谢谢。 简数采集器采集网站文章资讯等数据特别简单高效&#xff1a;只需输入网站网址&#xf…

全景vr交互微课视频开发让学习变得更加有趣、高效

在数字化教育的浪潮中&#xff0c;3D虚拟微课系统操作平台以其独特的魅力和创新的功能&#xff0c;成为吸引学生目光的焦点。这个平台不仅提供了引人入胜的画面和内容丰富的课件&#xff0c;更通过技术革新和制作方式的探索&#xff0c;将课程制作推向了一个全新的高度。 随着技…

HarmonyOS NEXT Developer Beta1配套相关说明

一、版本概述 2024华为开发者大会&#xff0c;HarmonyOS NEXT终于在万千开发者的期待下从幕后走向台前。 HarmonyOS NEXT采用全新升级的系统架构&#xff0c;贯穿HarmonyOS全场景体验的底层优化&#xff0c;系统更流畅&#xff0c;隐私安全能力更强大&#xff0c;将给您带来更高…