Почему приложение закрывается, когда сопрограмма пытается войти в Dispatchers.Main? - PullRequest
2 голосов
/ 12 апреля 2019

Я пытаюсь добавить сопрограмму, которая сначала устанавливает progressBar видимым, затем запрашивает данные у сервера, и, когда он получает данные, progressBar устанавливается невидимым. Я читал, что для взаимодействия с пользовательским интерфейсом моя сопрограмма должна работать в Dispatcher.Main, но когда я пытаюсь установить launch(Dispatcher.Main) целое приложение завершается без ошибки .

Я начал следующий урок с: https://www.kotlindevelopment.com/deep-dive-coroutines/. Я изменил деталь из кода, показанного там:

launch(UI) {
  progressBar.visibility = View.VISIBLE
  try {
    val userString = fetchUserString("1").await()
    val user = deserializeUser(userString).await()
    showUserData(user)
  } catch (ex: Exception) {
    log(ex)
  } finally {
    progressBar.visibility = View.GONE
  }
}

до:

GlobalScope.launch(Dispatchers.Main) {
                progressBarMarkers.visibility = View.VISIBLE
                try {
                    val repository = MarkerRepository()
                    points = repository.getAllDataAsync().await()
                    }
               } catch (ex: Exception) {
                    Log.d("EXCEPTION", ex.toString())
               } finally {
                    progressBarMarkers.visibility = View.INVISIBLE
                }
            }

но это не сработало. Я начал искать, в чем может быть проблема, и обнаружил, что когда мой сопрограмма выглядит так, как показано ниже, приложение завершает работу, когда оно достигает withContext(Dispatchers.Main)

GlobalScope.launch{
        val button = findViewById<ProgressBar>(R.id.progressBarMarkers)
        withContext(Dispatchers.Main){
                button.visibility = View.VISIBLE
        }
}

Я все еще очень плохо знаком с Kotlin и сопрограммами, так что, возможно, это просто какая-то основная ошибка, но я не смог найти ответ, почему приложение закрывается и что еще завершается без ошибки

Вся сопрограмма в:

class MapsActivity : AppCompatActivity(), OnMapReadyCallback {

    private lateinit var mMap: GoogleMap
    private val REQUEST_PERMISSION_CODE: Int = 123
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_maps)
        val mapFragment = supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment
        mapFragment.getMapAsync(this)

        ShowPlacesButton.setOnClickListener {
            launch{
                val button = findViewById<ProgressBar>(R.id.progressBarMarkers)
                withContext(Dispatchers.Main + Job()){
                    button.visibility = View.VISIBLE
                }
        }
    }
}

и части моего файла Gradle:

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.google.android.gms:play-services-maps:16.1.0'
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
    implementation 'com.google.dagger:dagger:2.13'
    kapt 'com.google.dagger:dagger-compiler:2.13'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
}

Почему приложение закрывается, когда оно достигает Dispatcher.Main?

1 Ответ

2 голосов
/ 14 апреля 2019

Возможно, вы включили «показывать только выбранное приложение» в logcat?Или какой-то другой фильтр?

Потому что, когда я запускаю ваш код, я получаю очень полезный сбой:

E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.idunnno.test, PID: 27501
    java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'
        at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.missing(MainDispatchers.kt:73)
        at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.isDispatchNeeded(MainDispatchers.kt:54)
        at kotlinx.coroutines.DispatchedKt.resumeCancellable(Dispatched.kt:373)
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:25)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:152)
        at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
        at com.idunnno.daggertest.MainActivity$onCreate$1$1.invokeSuspend(MainActivity.kt:27)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)

Основное исправление - добавить эту строку в ваши зависимости Gradle:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"

Некоторые другие примечания:

  1. Вы не хотите использовать findViewById внутри прослушивателя щелчков.Вы должны выполнить эту задачу один раз - вероятно, в onCreate - и затем сохранить ее в свойстве уровня класса.
  2. Вам не нужно использовать launch внутри этого прослушивателя щелчков.
  3. Если вы собираетесь использовать сопрограммы, то класс, вероятно, должен просто реализовать CoroutineScope.Это означает, что вам не нужно создавать новый контекст или использовать GlobalScope каждый раз, когда вы хотите использовать launch или async.
...