Котлин 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