java.lang.RuntimeException: методы, отмеченные @UiThread, должны выполняться в основном потоке. Текущий поток: DefaultDispatcher-worker-2

Я новичок в флаттере и котлине. Недавно делаю флаттер-версию (с 1.0.0 до 1.7.8+hotfix4). После того, как я обновил версию kotlin до 1.3.10, мое приложение flutter вылетало при попытке запустить его.

И ошибка выглядит так:

[ +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$1.success(MethodChannel.java:225) 
[   +1 ms] E/AndroidRuntime( 2726):     at XXXXXXXXXXXX.MainActivity$onActivityResult$1.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)
      }
    }
  }

1 ответ

Решение

Прежде всего вам следует избегать использования глобальной области видимости, это эквивалентно созданию потока в java, вы можете прочитать об этом здесь.

Во-вторых, вы должны знать, что вы можете изменять пользовательский интерфейс только в основном потоке. Скорее всегоmethodResult?.success(it) делает некоторые обновления пользовательского интерфейса, поэтому, если вы хотите быстрое исправление:

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 Ведьма означает, что он создаст рабочий поток, который обычно используется для интенсивных вычислений, если вы используете его для сетевых запросов, лучшим решением было бы запустить его с диспетчерским вводом-выводом:

// instead of GlobalScope.launch 
CoroutineScope(Dispatchers.IO).launch {
  // your code goes here
}
Другие вопросы по тегам