Android:全局异常处理 —— 入门指南

全局异常处理机制如同为App构建的"数字安全气囊",通过继承UncaughtExceptionHandler建立多级防护体系:精准捕捉所有未被捕获的程序异常,自动完成关键信息采集、云端日志上传等诊断流程,并执行可控的优雅退出策略。该机制有效规避Android系统默认的强制关闭弹窗(Application Not Responding),在保障用户体验连续性的同时,为开发者提供完整的异常全链路监控能力。

一、核心实现

1、自定义崩溃处理器(继承UncaughtExceptionHandler

kotlin 复制代码
// 示例代码
class GlobalCrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler {

    // 原生的默认处理器(最后要交给它处理)
    private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()

    override fun uncaughtException(thread: Thread, ex: Throwable) {
        // 1. 收集崩溃信息
        val crashReport = CrashReport(
            timestamp = System.currentTimeMillis(),
            deviceInfo = collectDeviceInfo(),
            stackTrace = ex.stackTraceToString(),
            threadInfo = thread.toString()
        )

        // 2. 处理崩溃日志
        saveCrashLocally(crashReport)
        uploadCrashAsync(crashReport)

        // 3. 主线程特殊处理
        if (thread == Looper.getMainLooper().thread) {
            handleUIThreadCrash()
        }

        // 4. 传递异常
        defaultHandler?.uncaughtException(thread, ex) ?: run {
            Process.killProcess(Process.myPid())
            System.exit(10)
        }
    }

    // 数据类用于封装崩溃信息
    data class CrashReport(
        val timestamp: Long,
        val deviceInfo: Map<String, String>,
        val stackTrace: String,
        val threadInfo: String
    )

    private fun collectDeviceInfo(): Map<String, String> = mapOf(
        "MANUFACTURER" to Build.MANUFACTURER,
        "MODEL" to Build.MODEL,
        "SDK_INT" to Build.VERSION.SDK_INT.toString(),
        "APP_VERSION" to context.packageManager.getPackageInfoCompat().versionName
    )
}

// 扩展函数获取PackageInfo
fun PackageManager.getPackageInfoCompat() = getPackageInfo(
    "your.package.name", 
    PackageManager.GET_ACTIVITIES
)

2、Application中注册全局异常处理器

kotlin 复制代码
// 示例代码
class MyApp : Application() {

    override fun onCreate() {
        super.onCreate()
        
        // 在应用初始化最后阶段注册
        setupExceptionHandler()
    }

    private fun setupExceptionHandler() {
        Thread.setDefaultUncaughtExceptionHandler(
            GlobalCrashHandler(applicationContext)
        )
    }
}

3、AndroidManifest中指定Application类

xml 复制代码
<application  
    android:name=".MyApp"  
    ...>  
</application>  

二、注意事项

1、避免异常被「吃掉」

  • 必须调用原有处理器的uncaughtException()
  • 不要直接catch (Throwable t)所有异常

2、第三方SDK的处理

  • 三方SDK可能会覆盖默认处理器(如:Firebase Crashlytics),需在初始化 SDK 后设置自定义处理器。

3、不要屏蔽异常

kotlin 复制代码
// 错误做法(会屏蔽默认行为)
override fun uncaughtException(thread: Thread, ex: Throwable) {
    // 没有调用默认处理器
}

// 正确做法:始终调用super或默认处理器

4、ANR处理(结合WatchDog)

kotlin 复制代码
// 示例代码
class ANRWatchDog : Thread() {
    override fun run() {
        while (!isInterrupted) {
            val tick = AtomicLong(0)
            Handler(Looper.getMainLooper()).post { tick.set(System.currentTimeMillis()) }
            
            sleep(5000)
            if (tick.get() < System.currentTimeMillis() - 5000) {
                // 触发ANR处理
            }
        }
    }
}

三、进阶技巧

1、本地存储

kotlin 复制代码
// 示例代码
private fun saveCrashLocally(report: CrashReport) {
    val logFile = File(context.filesDir, "crashes/${generateFileName()}")
    
    logFile.parentFile?.mkdirs()
    logFile.writeText(
        buildString {
            appendLine("====== CRASH REPORT ======")
            appendLine("Time: ${formatTimestamp(report.timestamp)}")
            report.deviceInfo.forEach { (k, v) -> appendLine("$k: $v") }
            appendLine("\nStack Trace:")
            append(report.stackTrace)
        }
    )
}

private fun generateFileName() = "crash_${SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(Date())}.log"

2、异步上传

kotlin 复制代码
// 示例代码
private fun uploadCrashAsync(report: CrashReport) {
    CoroutineScope(Dispatchers.IO).launch {
        try {
            CrashUploader.upload(report)
        } catch (e: Exception) {
            // 记录上传失败
        }
    }
}

3、主线程崩溃特殊处理

kotlin 复制代码
private fun handleUIThreadCrash() {
    // 关闭所有Activity
    (context as? Activity)?.run {
        finishAffinity()
    }
    
    // 启动恢复Activity
    val restartIntent = Intent(context, CrashRecoveryActivity::class.java).apply {
        flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
    }
    context.startActivity(restartIntent)
    
    // 延迟重启
    Handler(Looper.getMainLooper()).postDelayed({
        val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
        context.startActivity(launchIntent)
    }, 1500)
}

四、三方库推荐比对表

库名称 特点 适用场景
Firebase Crashlytics 实时报告,与Analytics集成 重度Google生态项目
Bugsnag 支持跨平台,代码片段追踪 多平台统一监控
ACRA 开源可定制,支持自建服务器 需要私有化部署的企业项目
Sentry 性能监控+错误追踪一体化 全链路监控需求

五、最佳实践建议

1、分层处理策略

kotlin 复制代码
// 不同层级处理异常
object ExceptionHandler {

    // UI层捕获
    fun handleUIException(ex: Exception) {
        showErrorToast(ex)
        logToAnalytics(ex)
    }

    // 数据层捕获
    fun handleDataLayerException(ex: Exception) {
        logToCrashlytics(ex)
        recoverData()
    }
}

2、关键路径保护

kotlin 复制代码
fun safeLaunch(block: () -> Unit) {
    try {
        block()
    } catch (ex: Exception) {
        GlobalCrashHandler.uncaughtException(Thread.currentThread(), ex)
    }
}

// 使用示例
fun fetchData() {
    safeLaunch {
        // 网络请求代码
    }
}

3、崩溃分析工具

kotlin 复制代码
// 使用AndroidX Startup初始化
class CrashAnalyticsInitializer : Initializer<Unit> {
    override fun create(context: Context) {
        setupCrashReporting()
    }

    private fun setupCrashReporting() {
        if (!BuildConfig.DEBUG) {
            FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
        }
    }
}

六、崩溃处理策略

复制代码
发生崩溃 → 收集设备信息 → 保存本地日志 → 上传服务器 → 
显示友好界面 → 尝试恢复 → 记录崩溃次数 → 达到阈值触发强制更新

扩展建议

  1. 使用ProcessPhoenix库实现干净重启
  2. 结合WorkManager实现失败日志的自动重传
  3. 使用Timber进行增强日志记录
  4. 通过Debug.getRuntimeStats()获取更多运行时信息

此实现方案支持以下特性:

  • 协程异步处理
  • 扩展函数增强可读性
  • 空安全类型系统
  • 与Jetpack组件深度集成
  • 支持多进程应用

可根据具体需求调整崩溃处理策略,建议在debug构建中启用严格模式检测潜在问题。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 委托与扩展函数------新手入门
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
  6. Kotlin 扩展方法(Extension Functions)使用详解
  7. Kotlin 中 == 和 === 的区别
  8. Kotlin 操作符与集合/数组方法详解------新手指南
相关推荐
落——枫1 小时前
网络安全知识点
安全·web安全
三品PLM系统2 小时前
三品PLM研发管理软件如何构筑制造企业全产品生命周期管理?
大数据·运维·人工智能·安全·制造
big凉笙墨染2 小时前
Web安全小白看不懂HTTP协议?一文带你精通HTTP基础知识,Web安全入门必读【娱乐篇】
前端·安全·黑客
开开心心就好2 小时前
手机不同App音量自动调节软件
网络·windows·python·安全·智能手机·电脑·音视频
计算机鬼才~2 小时前
网络安全·第一天·IP协议安全分析
网络协议·安全·网络安全
智联视频超融合平台2 小时前
国网B接口注册流程详解以及注册失败原因(电网B接口)
网络·人工智能·后端·网络协议·安全·音视频·实时音视频
秋说3 小时前
【区块链安全 | 第三十五篇】溢出漏洞
安全·区块链
iOS阿玮3 小时前
2025年第一季度3.2f求助排名第一,你还敢违规操作么?
前端·app·apple
Blossom.1183 小时前
物联网安全技术:守护智能世界的防线
人工智能·深度学习·物联网·安全·机器学习·自动化·去中心化