网站短期就业培训班,搜集关键词的网站,如何制作企业的网站,南山出名的互联网公司背景#xff1a; 在Android app运行中#xff0c;有时一些无关紧要的异常出现时希望App 不崩溃#xff0c;能继续让用户操作#xff0c;可以有效提升用户体验和增加业务价值。 新流程#xff1a; 哪些场景需要Catch Crash Config配置信息#xff1a;
支持从网络上获… 背景 在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: ListString emptyList(),SerializedName(app_version)val appVersion: ListString emptyList(),SerializedName(os_version)val osVersion: ListInt emptyList(),val model: ListString 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: ListCrashPortray? 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(): ListCrashPortray {// 从网络获取crash配置val crashList AppConfigLib.getCrashPortrays() ?: ArrayList()//添加本地默认配置crashList.apply {this.addAll(getSystemException())this.addAll(getRNException())this.addAll(getSDKException())}return crashList}// 三方sdk异常private fun getSDKException(): ListCrashPortray {val crashList mutableListOfCrashPortray()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(): ListCrashPortray {val crashList mutableListOfCrashPortray()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(): ListCrashPortray {val crashList mutableListOfCrashPortray()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: ListCrashPortray? 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 truereturnforEach}}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)
}