Беглый взгляд на Async-Await в Android

от автора

От переводчика

Это мой первый перевод, поэтому прошу прощения за неточности. Если вы найдете ошибки в переводе, пожалуйста сообщите об этом. Я не нашел лучшего перевода слова сoroutine, чем сопрограмма, поэтому решил использовать оригинал. Если у вас появятся идеи по этому поводу, буду рад узнать.

Kotlin версии 1.1 принесет в язык coroutin’ы, которые позволяют приостанавливать вычисления в какой-то точке а затем продолжить их позднее. Очевидный пример этой возможности — async-await, который был добавлен в C# несколько лет назад.

Каждый android разработчик знает, что когда мы имеем дело с сетевыми запросами или другими I/O задачами, то нам необходимо удостовериться, что не происходит блокировка основного потока, а так же, что мы не трогаем UI из фонового потока. На протяжении многих лет приходят и уходят десятки приемов. В этой статье перечислены наиболее популярные, и показаны примеры удобства, которое несет с собой async-await.

Сценарий

Мы хотим получить данные пользователя Github и положить их в базу данных. Когда это будет сделано, мы покажем результат на экране. Я не хочу объяснять подходы, они должны сказать всё сами за себя.

Старый добрый Thread

Ручное управление, полный контроль

fun threads() {   val handler = Handler()    Thread {     try {       val user = githubApi.user()       userRepository.store(user)       handler.post {         threadsTV.text = "threads: [$user]"       }     } catch(e: IOException) {       handler.post {         threadsTV.text = "threads: [User retrieval failed.]"       }     }   }.start() }

AsyncTask андроида

Никто же их не использует больше, верно?

fun asyncTask() {   object : AsyncTask<Unit, Unit, GithubUser?>() {      private var exception: IOException? = null      override fun doInBackground(vararg params: Unit): GithubUser? {       try {         val user = githubApi.user()         userRepository.store(user)         return user       } catch(e: IOException) {         exception = e         return null       }     }      override fun onPostExecute(user: GithubUser?) {       if (user != null) {         asyncTaskTV.text = "asyncTask: [$user]"       } else {         asyncTaskTV.text = "asyncTask: [User retrieval failed.]"       }     }   }.execute() }

Callbacks

А Callback-hell кто-ниубдь использует?

fun callbacks() {   githubApi.userFromCall().enqueue(object : Callback<GithubUser> {      override fun onResponse(call: Call<GithubUser>, response: Response<GithubUser>) {       val user = response.body()       userRepository.storeCallback(user) {         callbacksTV.text = "callbacks: [$user]"       }     }      override fun onFailure(call: Call<GithubUser>, t: Throwable) {       if (t is IOException)         callbacksTV.text = "callbacks: [User retrieval failed.]"       else         throw t     }   }) }

Rx

Предоставляет крутые вещи…

fun rx() {   githubApi.userRx()       .flatMap { user ->         userRepository.storeRx(user).toSingle { user }       }       .observeOn(AndroidSchedulers.mainThread())       .subscribe(           { user ->             rxTV.text = "rx: [$user]"           },           { throwable ->             if (throwable is IOException)               rxTV.text = "rx: [User retrieval failed.]"             else               throw throwable           }       ) }

Async-Await

А как вы смотрите на это?

fun asyncAwait() = asyncUI {   try {     val user = await(githubApi.userAsync())     await(userRepository.storeAsync(user))     asyncAwaitTV.text = "asyncAwait: [$user]"   } catch(e: IOException) {     asyncAwaitTV.text = "asyncAwait: [User retrieval failed.]"   } }

Тут asyncUI (и аналогичный async<Т>) метод включет функционал coroutin’ы, который предоставляет доступ к методу await. Каждый раз, когда выполнение достигает метода await, вычисления приостанавливаются до тех пор, пока параметр не будет вычислен, но поток, в котором произошел вызов, не блокируется. После этого coroutine продолжит свое выполнение. Метод asyncUI гарантирует, что выполнение продолжится в главном потоке.


Как вы заметили, coroutine повышает читаемость кода. Они доступны уже сейчас в версии kotlin 1.1-M02. Последний пример async-await использует библиотеку, которую я написал для возможности использования coroutines на Android. Если хотите больше узнать о coroutin’ах, то можете ознакомиться с неформальным описанием

PS: Эта статья не содержит отмены выполнений и удаления слушателей, которые могут содержать ссылки на активити. Каждый подход может иметь схожий вариант, но без утечек.

В ближайшее время появится перевод продолжения.

ссылка на оригинал статьи https://habrahabr.ru/post/314574/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *