You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Thread.UncaughtExceptionHandler调用navigate()致App无响应问题求助

全局异常捕获后调用导航导致应用无响应的问题解决

问题场景

通过DmsExceptionHandler实现全局异常捕获,但捕获异常后调用navigateToSignIn()导航到登录页时,应用未崩溃却停止响应:UI组件(如底部导航栏)可点击并显示波纹,但无法切换页面。若不在uncaughtException中调用导航方法,应用不会出现该问题。

相关代码

异常处理器代码:

internal class DmsExceptionHandler @Inject constructor(
    private val context: Context,
    private val appState: DmsAppState,
) : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(
        thread: Thread,
        exception: Throwable,
    ) {
        when (exception) {
            is CommonException.SignInRequired -> signInRequired()
            else -> {
                signInRequired() // todo replace
            }
        }
    }

    private fun signInRequired() {
        appState.navigateToSignIn()
    }
}

导航逻辑代码:

internal fun DmsAppState.navigateToSignIn() {
    println("NAVIGATED TO SIGN IN")
    this.navController.navigate(DmsRoute.Auth.route) {/*
        popUpTo(this@navigateToSignIn.navController.graph.startDestinationId) {
            saveState = true
        }*/
    }
}

问题原因

  1. 线程状态异常:uncaughtException方法在抛出异常的线程(通常是主线程)中执行,此时主线程已因未处理异常处于不稳定状态,消息队列的正常循环被打断。直接在该方法内执行导航这类UI操作,会导致主线程无法恢复正常事件循环。
  2. 未调用默认处理器:当前异常处理器覆盖了系统默认逻辑,但未调用默认的未捕获异常处理器,导致系统无法完成线程的异常清理,进一步加剧线程状态异常。
  3. 导航上下文无效:异常状态下的主线程无法正常处理导航栈更新、视图渲染等逻辑,仅能响应View本地的点击波纹动画。

解决方案

方案1:通过Handler调度导航并调用默认处理器

修改异常处理器,将导航操作通过Handler post到主线程正常消息队列,同时调用系统默认处理器完成线程清理:

internal class DmsExceptionHandler @Inject constructor(
    private val context: Context,
    private val appState: DmsAppState,
) : Thread.UncaughtExceptionHandler {

    // 保存系统默认异常处理器
    private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()

    override fun uncaughtException(
        thread: Thread,
        exception: Throwable,
    ) {
        when (exception) {
            is CommonException.SignInRequired -> handleSignInRequired()
            else -> handleSignInRequired() // 替换为你的其他异常处理逻辑
        }
        // 必须调用默认处理器,让系统完成线程异常清理
        defaultHandler?.uncaughtException(thread, exception)
    }

    private fun handleSignInRequired() {
        // 将导航操作post到主线程正常消息队列
        Handler(Looper.getMainLooper()).post {
            appState.navigateToSignIn()
        }
    }
}

方案2:使用Coroutine在主线程安全执行导航

若项目使用Coroutine,可通过MainScope调度导航操作,效果与Handler一致:

internal class DmsExceptionHandler @Inject constructor(
    private val context: Context,
    private val appState: DmsAppState,
) : Thread.UncaughtExceptionHandler {

    private val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()
    private val mainScope = MainScope()

    override fun uncaughtException(
        thread: Thread,
        exception: Throwable,
    ) {
        when (exception) {
            is CommonException.SignInRequired -> handleSignInRequired()
            else -> handleSignInRequired()
        }
        defaultHandler?.uncaughtException(thread, exception)
    }

    private fun handleSignInRequired() {
        mainScope.launch {
            appState.navigateToSignIn()
        }
    }
}

额外优化:完善导航安全检查

在导航逻辑中增加状态检查,避免无效导航:

internal fun DmsAppState.navigateToSignIn() {
    println("NAVIGATED TO SIGN IN")
    // 检查导航控制器是否活跃,避免重复导航
    if (navController.currentDestination?.id != DmsRoute.Auth.id) {
        navController.navigate(DmsRoute.Auth.route) {
            popUpTo(navController.graph.startDestinationId) {
                saveState = true
            }
            launchSingleTop = true
        }
    }
}

内容的提问来源于stack exchange,提问作者JunJaBoy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 15:10:07