Я довольно привык использовать RX для обработки параллелизма, но в моей текущей работе у нас есть сочетание AsyncTask, Executors + Handlers, Threads и некоторых LiveData. Теперь мы думаем о переходе к использованию Kotlin Сопрограммы (и фактически начали использовать их в определенных местах в кодовой базе).
Поэтому мне нужно начать с головой окунуться в сопрограммы, в идеале опираясь на мои существующие знания об инструментах параллелизма, чтобы ускорить процесс.
Я пробовал следовать за ними в Google codelab, и, хотя он дает мне некоторое понимание, он также вызывает множество вопросов без ответов, поэтому я попытался испачкать руки, написав код, отладив и просмотрев журнал выводит.
Насколько я понимаю, сопрограмма состоит из двух основных строительных блоков; функции приостановки, в которых вы выполняете свою работу, и контексты сопрограмм, в которых вы выполняете функции приостановки, чтобы вы могли контролировать, на каких диспетчерах будут работать сопрограммы.
Здесь у меня есть код ниже, который ведет себя как я и ожидал. Я установил контекст сопрограммы с помощью Dispatchers.Main. Итак, как и ожидалось, когда я запускаю сопрограмму getResources
, она блокирует поток пользовательского интерфейса на 5 секунд из-за Thread.sleep(5000)
:
private const val TAG = "Coroutines"
class MainActivity : AppCompatActivity(), CoroutineScope {
override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
log("onCreate", "launching coroutine")
launch {
val resource = getResource()
log("onCreate", "resource fetched: $resource")
findViewById<TextView>(R.id.textView).text = resource.toString()
}
log("onCreate", "coroutine launched")
}
private suspend fun getResource() : Int {
log("getResource", "about to sleep for 5000ms")
Thread.sleep(5000)
log("getResource", "finished fetching resource")
return 1
}
private fun log(methodName: String, toLog: String) {
Log.d(TAG,"$methodName: $toLog: ${Thread.currentThread().name}")
}
}
Когда я запускаю этот код, я вижу следующие журналы :
2020-05-28 11:42:44.364 9819-9819/? D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:42:44.376 9819-9819/? D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:42:44.469 9819-9819/? D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:42:49.471 9819-9819/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:42:49.472 9819-9819/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main
Как видите, все журналы исходят из основного потока, и между журналами до и после Thread.sleep(5000)
есть 5-секундный промежуток. Во время этого 5-секундного перерыва поток пользовательского интерфейса блокируется, я могу подтвердить это, просто взглянув на эмулятор; он не отображает какой-либо пользовательский интерфейс, потому что onCreate
заблокирован.
Теперь, если я обновлю функцию getResources
, чтобы использовать функцию приостановки delay(5000)
вместо использования Thread.sleep(5000)
вот так:
private suspend fun getResource() : Int {
log("getResource", "about to sleep for 5000ms")
delay(5000)
log("getResource", "finished fetching resource")
return 1
}
Тогда то, что я вижу, меня смущает. Я понимаю, что delay
- это не то же самое, что Thread.sleep
, но, поскольку я запускаю его в контексте сопрограммы, который поддерживается Dispatchers.Main
, я ожидал увидеть тот же результат, что и при использовании Thread.sleep
.
Вместо этого я вижу, что поток пользовательского интерфейса не заблокирован, пока происходит 5-секундная задержка, а журналы выглядят так:
2020-05-28 11:54:19.099 10038-10038/com.example.coroutines D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:54:19.111 10038-10038/com.example.coroutines D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:54:19.152 10038-10038/com.example.coroutines D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:54:24.167 10038-10038/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:54:24.168 10038-10038/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main
Я вижу, что поток пользовательского интерфейса не заблокирован в этом случае, поскольку пользовательский интерфейс отображается, пока происходит задержка, а затем текстовое представление обновляется через 5 секунд.
Итак, мой вопрос: как задержка в этом случае не блокирует поток пользовательского интерфейса (даже если журналы в моей функции приостановки все еще указывается, что функция работает в основном потоке ...)