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

Android协程回调调用Google Ads触发WebView线程错误如何修复?

安卓协程调用广告线程异常修复方案

问题背景

部分用户使用时出现如下崩溃:

kotlinx.coroutines.CompletionHandlerException: Exception in completion handler InvokeOnCompletion@c1de63[job@7e00a60] for StandaloneCoroutine{Completed}@7e00a60
    at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:320)
    at kotlinx.coroutines.JobSupport.tryFinalizeSimpleState(JobSupport.kt:295)
    at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:856)
    at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:828)
    at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:100)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'DefaultDispatcher-worker-5'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 2) {8812c5f} called on null, FYI main Looper is Looper (main, tid 2) {8812c5f})
    at android.webkit.WebView.checkThread(WebView.java:2603)
    at android.webkit.WebView.evaluateJavascript(WebView.java:888)
    at com.google.android.gms.ads.internal.webview.ac.evaluateJavascript(:com.google.android.gms.policy_ads_fdr_dynamite@213806100@213806100057.397311113.397311113:3)
    // 省略中间堆栈
    at com.app_name.utils.ads.Ads.showInter(Ads.kt:73)
    at com.app_name.ui.FragmentDeleting$jProgressBarAnimate$1$1$1$onAnimationEnd$1.invoke(FragmentDeleting.kt:105)
    at com.app_name.ui.FragmentDeleting$jProgressBarAnimate$1$1$1$onAnimationEnd$1.invoke(FragmentDeleting.kt:102)
    at kotlinx.coroutines.InvokeOnCompletion.invoke(JobSupport.kt:1391)
    at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:318)
    ... 10 more
Caused by: java.lang.Throwable: A WebView method was called on thread 'DefaultDispatcher-worker-5'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 2) {8812c5f} called on null, FYI main Looper is Looper (main, tid 2) {8812c5f})
    at android.webkit.WebView.checkThread(WebView.java:2598)
    ... 33 more

现有业务代码如下:

  1. 删除功能协程代码:
val deletingJob = lifecycleScope.launch(Dispatchers.IO) {
    viewModel.selectFilesDeletion { _, _, _ -> }
}
  1. 动画结束调用广告逻辑:
val animProgress = ObjectAnimator.ofInt(progressBar, "progress", progressBar.progress, PROGRESS_MAX)
animProgress.apply {
    duration = DURATION_ANIMATION
    setAutoCancel(true)
    interpolator = DecelerateInterpolator()
    start()
    doOnEnd {
        binding.lottieAnimation.addAnimatorListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(p0: Animator?) {}
            override fun onAnimationCancel(p0: Animator?) {}
            override fun onAnimationRepeat(p0: Animator?) {}
            override fun onAnimationEnd(animator: Animator) {
                if (deletingJob.isCompleted) {
                        Ads.showInter(requireActivity(), this@FragmentDeleting)
                } else {
                    deletingJob.invokeOnCompletion {
                            Ads.showInter(requireActivity(), this@FragmentDeleting)
                    }
                }
            }
        })
        binding.lottieAnimation.playAnimation()
    }
}

问题解答

1. 包runOnUiThread是否能解决问题?

可以。所有WebView相关的API必须在主线程调用,requireActivity().runOnUiThread{ }会将包裹的逻辑强制投递到主线程执行,完全符合系统要求,能直接修复该崩溃。

2. 是否需要改写现有协程逻辑?问题根源是什么?

不需要完全改写现有逻辑,问题确实是线程调用不符合要求导致的:

  • 当动画结束时删除任务已经执行完成,走deletingJob.isCompleted为true的分支,这部分代码运行在动画回调的主线程,调用广告没有问题
  • 当动画结束时删除任务还在执行,走invokeOnCompletion回调,这个回调的执行线程是协程完成时所在的线程,也就是Dispatchers.IO对应的子线程,这时候调用广告内部的WebView API就触发了线程检查异常

可选修复方案

两种方案任选即可:

方案1:最小修改,给广告调用加主线程包裹

两个调用点都加上主线程调度:

override fun onAnimationEnd(animator: Animator) {
    if (deletingJob.isCompleted) {
        requireActivity().runOnUiThread {
            Ads.showInter(requireActivity(), this@FragmentDeleting)
        }
    } else {
        deletingJob.invokeOnCompletion {
            requireActivity().runOnUiThread {
                Ads.showInter(requireActivity(), this@FragmentDeleting)
            }
        }
    }
}
方案2:协程写法优化,更简洁规范

不用手动判断任务状态和切线程,用协程的join方法更符合协程使用习惯:

override fun onAnimationEnd(animator: Animator) {
    lifecycleScope.launch {
        // 挂起等待删除任务完成,不会阻塞主线程
        deletingJob.join()
        // lifecycleScope默认运行在主线程,直接调用广告即可
        Ads.showInter(requireActivity(), this@FragmentDeleting)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 19:36:02