java.lang.RuntimeException: @UiThread 标记的方法必须在主线程上执行。当前线程:DefaultDispatcher-worker-2

java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: DefaultDispatcher-worker-2

我对 flutter 和 kotlin 很陌生。我最近正在升级 flutter 版本(从 1.0.0 到 1.7.8+hotfix4)。我升级kotlin版本到1.3.10后,我的flutter app在尝试启动的时候崩溃了

错误显示如下:

[ +521 ms] E/AndroidRuntime( 2726): FATAL EXCEPTION: DefaultDispatcher-worker-2
[+1 ms] E/AndroidRuntime( 2726): Process: XXXXXXXXXXXX, PID: 2726 
[        ] E/AndroidRuntime( 2726): java.lang.RuntimeException: Methods marked with @UiThread must be executed on the main thread. Current thread: DefaultDispatcher-worker-2
[        ] E/AndroidRuntime( 2726):     at io.flutter.embedding.engine.FlutterJNI.ensureRunningOnMainThread(FlutterJNI.java:794) 
[   +1 ms] E/AndroidRuntime( 2726):     at io.flutter.embedding.engine.FlutterJNI.invokePlatformMessageResponseCallback(FlutterJNI.java:727) 
[   +1 ms] E/AndroidRuntime( 2726):     at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:140) 
[   +1 ms] E/AndroidRuntime( 2726):     at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.success(MethodChannel.java:225) 
[   +1 ms] E/AndroidRuntime( 2726):     at XXXXXXXXXXXX.MainActivity$onActivityResult.invokeSuspend(MainActivity.kt:91) 
[        ] E/AndroidRuntime( 2726):     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
[        ] E/AndroidRuntime( 2726):     at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241) 
[        ] E/AndroidRuntime( 2726):     at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594) 
[        ] E/AndroidRuntime( 2726):     at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60) 
[        ] E/AndroidRuntime( 2726):     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:740) 
[  +14 ms] D/AutoManageHelper( 2726): onStart true {99992=com.google.android.gms.internal.zzbau$zza@72d039a} 
[ +983 ms] I/CrashlyticsCore( 2726): Crashlytics report upload complete: 5D72013C01D2-0001-0AA6-12B9D92A3973

这是build.gradle中的设置:

ext.kotlin_coroutines_version = "1.3.0"
ext.okhttp_version = "3.12.0"

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlin_coroutines_version"

这是MainActivity中的代码:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
      LOGIN_CODE ->
        if (resultCode == RESULT_OK) {
          if (LoginManager.getInstance().loginService.isLoggedIn) {
            GlobalScope.launch {
              async {
                var ls = LoginManager.getInstance().loginService
                var response = ls.authRequest<AuthResponse<TokenResult>>(AUTH_PROVIDER_NAME)
                return@async response.getToken()
              }.await().let {
                methodResult?.success(it)
              }
            }
          }
        } else {
          methodResult?.success("")
        }

      LOGOUT_CODE ->
        if (resultCode == RESULT_OK) {
          methodResult?.success(true)
        }

      REQUEST_CODE_TAKE_IMAGE_WITH_CAMERA -> {
        if (resultCode != RESULT_OK) {
          methodResult?.success("")
        }
      }

      else -> {
        methodResult?.success(false)
      }
    }
  }

首先你应该避免使用全局作用域,它相当于在java中创建一个线程,你可以阅读更多here关于这个的内容。

其次,您应该知道只能在主线程上修改 UI。很可能 methodResult?.success(it) 做了一些 UI 更新,所以如果你想要快速修复:

GlobalScope.launch {
              async {
                var ls = LoginManager.getInstance().loginService
                var response = ls.authRequest<AuthResponse<TokenResult>>(AUTH_PROVIDER_NAME)
                return@async response.getToken()
              }.await().let {
                withContext(Dispatchers.Main) {
                  methodResult?.success(it)
                }
              }
            }

当您调用 GlobalScope.launch 时,它意味着您启动协程,因为 Dispatchers.Default 意味着它将创建一个通常用于密集计算的工作线程,如果您使用它对于网络请求,更好的解决方案是使用调度程序 IO 启动它:

// instead of GlobalScope.launch 
CoroutineScope(Dispatchers.IO).launch {
  // your code goes here
}