Котлин Coroutines правильный путь в Android
Я пытаюсь обновить список внутри адаптера с помощью async, я вижу, что слишком много шаблона.
правильно ли использовать Kotlin Coroutines?
можно ли оптимизировать это больше?
fun loadListOfMediaInAsync() = async(CommonPool) {
try {
//Long running task
adapter.listOfMediaItems.addAll(resources.getAllTracks())
runOnUiThread {
adapter.notifyDataSetChanged()
progress.dismiss()
}
} catch (e: Exception) {
e.printStackTrace()
runOnUiThread {progress.dismiss()}
} catch (o: OutOfMemoryError) {
o.printStackTrace()
runOnUiThread {progress.dismiss()}
}
}
7 ответов
после борьбы с этим вопросом в течение нескольких дней, я думаю, что самый простой и понятный шаблон асинхронного ожидания для Android-действий с помощью Kotlin:
override fun onCreate(savedInstanceState: Bundle?) {
//...
loadDataAsync(); //"Fire-and-forget"
}
fun loadDataAsync() = async(UI) {
try {
//Turn on busy indicator.
val job = async(CommonPool) {
//We're on a background thread here.
//Execute blocking calls, such as retrofit call.execute().body() + caching.
}
job.await();
//We're back on the main thread here.
//Update UI controls such as RecyclerView adapter data.
}
catch (e: Exception) {
}
finally {
//Turn off busy indicator.
}
}
единственный зависимостей Gradle в для сопрограммы являются: kotlin-stdlib-jre7
, kotlinx-coroutines-android
.
Примечание: использовать job.await()
вместо job.join()
, потому что await()
возвращает исключения, но join()
нет. Если вы используете join()
вам нужно будет проверить job.isCompletedExceptionally
после завершения задания.
начать одновременно retrofit звонки, вы можете сделать это:
val jobA = async(CommonPool) { /* Blocking call A */ };
val jobB = async(CommonPool) { /* Blocking call B */ };
jobA.await();
jobB.await();
или:
val jobs = arrayListOf<Deferred<Unit>>();
jobs += async(CommonPool) { /* Blocking call A */ };
jobs += async(CommonPool) { /* Blocking call B */ };
jobs.forEach { it.await(); };
как запустить корутину
на kotlinx.coroutines
библиотека вы можете запустить новый coroutine, используя либо launch
или был призван dataProvider.loadData
все еще продолжалось, функция view.showData
никогда не будет вызван.
var job: Job? = null
fun startPresenting() {
job = loadData()
}
fun stopPresenting() {
job?.cancel()
}
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await() // non ui thread, suspend until finished
view.showData(result) // ui thread
}
полный ответ доступен в моей статье Android Coroutine Рецепты
Я думаю, вы можете избавиться от runOnUiThread { ... }
с помощью UI
контекст для приложений Android вместо CommonPool
.
на UI
контекст, предусмотренных kotlinx-coroutines-android.
у нас также есть другой вариант. если мы используем Анко библиотека, тогда это выглядит так
doAsync {
// Call all operation related to network or other ui blocking operations here.
uiThread {
// perform all ui related operation here
}
}
добавить зависимость для Anko в вашем приложении gradle, как это.
compile "org.jetbrains.anko:anko:0.10.3"
как сказал sdeff, если вы используете контекст пользовательского интерфейса, код внутри этой подпрограммы будет работать в потоке пользовательского интерфейса по умолчанию. И, если вам нужно запустить инструкцию в другом потоке, вы можете использовать run(CommonPool) {}
кроме того, если вам не надо ничего возвращать из метода, вы можете использовать функцию launch(UI)
вместо async(UI)
(бывшая вернет Job
и последний Deferred<Unit>
).
например:
fun loadListOfMediaInAsync() = launch(UI) {
try {
withContext(CommonPool) { //The coroutine is suspended until run() ends
adapter.listOfMediaItems.addAll(resources.getAllTracks())
}
adapter.notifyDataSetChanged()
} catch(e: Exception) {
e.printStackTrace()
} catch(o: OutOfMemoryError) {
o.printStackTrace()
} finally {
progress.dismiss()
}
}
Если вам нужна дополнительная помощь, я рекомендую вам читать главное руководство kotlinx.coroutines и, кроме того, руководство coroutines + UI
Если вы хотите вернуть что-то из фонового потока, используйте async
launch(UI) {
val result = async(CommonPool) {
//do long running operation
}.await()
//do stuff on UI thread
view.setText(result)
}
Если фоновый поток, ничего не возвращается
launch(UI) {
launch(CommonPool) {
//do long running operation
}.await()
//do stuff on UI thread
}
все вышеперечисленные ответы верны, но мне было трудно найти правильный импорт для UI
С kotlinx.coroutines
, это противоречило UI
С Anko
.
Его
import kotlinx.coroutines.experimental.android.UI