Android runOnUiThread中dismiss Dialog报视图线程错误如何解决
问题根因
"Only the original thread that created a view hierarchy can touch its views." 异常的触发和你有没有用runOnUiThread没有必然关系,核心判定规则是:所有View/Dialog的操作,必须在创建它的原始线程执行。
你当前代码报错的直接原因:
- 要么
appleLoginDialog实例本身是在子线程中创建的,你强行通过runOnUiThread切到当前Activity的主线程去执行isShowing判断、dismiss操作,操作线程和创建线程不一致 - 要么Dialog持有的是已经销毁的旧Activity的视图层级引用,你当前的
runOnUiThread调度到的是新的Activity主线程上下文,和Dialog创建时绑定的线程不匹配 - 额外注意:
dialog.isShowing本质也是对视图内部状态的读取,属于视图操作范畴,一样受线程规则限制,不是只有dismiss()才会触发校验。
修复方案
- 强制所有Dialog的初始化、show、dismiss全流程在主线程执行
禁止在任何子线程中new Dialog实例、调用Dialog的show/dismiss方法,Dialog初始化必须使用当前存活的Activity作为Context,不要用Application Context、其他线程初始化的Context创建Dialog。
参考实现:private var appleLoginDialog: Dialog? = null // 在Activity主线程生命周期中初始化Dialog override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) appleLoginDialog = AlertDialog.Builder(this) .setTitle("苹果登录") .setView(R.layout.dialog_apple_login) .setCancelable(false) .create() } // 统一封装Dialog关闭逻辑 private fun dismissAppleLoginDialog() { val action = { appleLoginDialog?.takeIf { it.isShowing }?.dismiss() } // 仅当当前不在主线程时才切线程 if (Looper.myLooper() != Looper.getMainLooper()) { runOnUiThread(action) } else { action() } } - 生命周期绑定避免泄漏
在Activity的onDestroy回调中主动关闭Dialog并清空引用,避免持有旧视图树的Dialog实例被后续逻辑误调用:override fun onDestroy() { appleLoginDialog?.takeIf { it.isShowing }?.dismiss() appleLoginDialog = null super.onDestroy() } - 特殊场景说明
如果你确实在子线程创建了带独立Looper的Dialog(极不推荐,会导致弹窗脱离Activity生命周期管控),那么所有对该Dialog的操作都必须通过创建它的线程对应的Handler调度,不能使用当前Activity的runOnUiThread。
内容的提问来源于stack exchange,提问作者Juro
相关产品推荐
相关产品推荐

