Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Coroutines: Como solución para tu aplicación

Coroutines: Como solución para tu aplicación

C6e1201f51c1ff186edba69f98476f15?s=128

Dinorah Tovar

August 10, 2019
Tweet

Transcript

  1. Coroutines: Como solución en tu aplicación Dinorah Tovar Head of

    Android Development at Lonely Planet
  2. Coroutines Son componentes para crear subtareas multirutina, que pueden ser

    suspendidas y reanudadas. 
 Una manera más fácil de decir que en Kotlin tenemos formas de hace Suspendable computation Disponible para todas los lugares donde Kotlin esta soportado. Mobile, Cloud, Multi-platform o Web
  3. Aplicaciones

  4. Aplicaciones

  5. Asynchronous programming var user = userRemoteDataSource.getUser() titleUserName?.text = user?.name

  6. Asynchronous programming var user = userRemoteDataSource.getUser() titleUserName?.text = user?.name

  7. Asynchronous programming thread(start = true) { var user = userRemoteDataSource.getUser()

    titleUserName?.text = user?.name }
  8. Asynchronous programming thread(start = true) { var user = userRemoteDataSource.getUser()

    titleUserName?.text = user?.name }
  9. Asynchronous programming userRemoteDataSource.getUser() .subscribe(object : ResourceObserver<User>() { override fun onComplete()

    { } override fun onNext(value: User) { titleUserName?.text = user?.name } override fun onError(e: Throwable) { } }) Out of memory exception
  10. Asynchronous programming userRemoteDataSource.getUser() .subscribe(object : ResourceObserver<User>() { override fun onComplete()

    { disposable() } override fun onNext(value: User) { titleUserName?.text = user?.name } override fun onError(e: Throwable) { disposable() } })
  11. Asynchronous programming userRemoteDataSource.getUser() .set(autoDisposable(AndroidLifecycleScopeProvider.from(this))) .subscribe(...)

  12. Asynchronous programming fun getUser(): Observable<User> { //Return observable } fun

    getToken(): Observable<Token> { //Return observable } fun subscribeNotification(): Observable<Boolean> { //Return observable }
  13. Asynchronous programming fun getUser(): Observable<User> {…} fun getToken(): Observable<Token> {…}

    fun subscribeNotification(): Observable<Boolean> {…} fun loginUser() { getUser() getToken() subscribeNotification() }
  14. Asynchronous programming private fun getToken(): Single<Token>> { ... } private

    fun subscribeNotifications(): Single<Boolean> { return userRemoteDataSource.subscribeNotifications() .flatMap { boolean -> userRemoteDataSource.subscribeOther .subscribeOn(...) }
  15. Asynchronous programming fun loginUser() { val rxToken = getToken() val

    rxNotifications = subscribeNotifications() Single.zip(rxToken, rxNotifications, BiFunction<Token, Boolean, Token> { getUser() }) }
  16. RxJava Es una librería muy poderosa, todo mundo la ha

    usado, pero no es una librería para manejar async work. Es una librería de procesamiento de eventos.
  17. Coroutines Muchos creen que aunque podría solucionar el problema, le

    falta madurez, y algunos desarrolladores creen que representa una curva de aprendizaje. Robusto Simple Readable
  18. Coroutines suspend fun initCoroutine() { val jobs = List(100_000) {

    GlobalScope.launch { delay(1000) Log.d (" . ") } } jobs.forEach{ it.join() } } Coroutines son parecidos a thread
  19. Coroutines suspend fun initCoroutine() { val jobs = List(100_000) {

    GlobalScope.launch { delay(1000) Log.d (" . ") } } jobs.forEach{ it.join() } } Coroutines son parecidos a light-weight thread
  20. Coroutines suspend fun initCoroutine() { val jobs = List(100_000) {

    GlobalScope.launch { delay(1000) Log.d (" . ") } } jobs.forEach{ it.join() } } Coroutines son parecidos a light-weight thread
  21. Coroutines Coroutines son parecidos a light-weight thread Thread pueden ocupar

    1-2Mb Thread management Thread Pools
  22. Coroutines suspend fun initCoroutine() { val jobs = List(100_000) {

    GlobalScope.launch { delay(1000) Log.d (" . ") } } jobs.forEach{ it.join() } } Coroutines son parecidos a light-weight thread
  23. Coroutines Coroutines son parecidos a light-weight thread Coroutines son State

    Machine objects Data for each state Y puedes pausarlos y reanudarlos
  24. Coroutines Coroutines builders son bridges Synchronous Moment Suspendable
 function Coroutine

    Builder
  25. Coroutines userRemoteDataSource.getUser() .subscribe(object : ResourceObserver<User>() { override fun onComplete() {

    } override fun onNext(value: User) { titleUserName?.text = user?.name } override fun onError(e: Throwable) { } })
  26. Coroutines launch (dispatcher) { userRemoteDataSource.getUser() }

  27. Coroutines launch (dispatcher) { userRemoteDataSource.getUser() withContext(anotherDispatcher) { titleUserName?.text = user?.name

    } }
  28. Coroutines Dispatchers Multiple flavors Dispatchers.Main Main thread, UI, solo trabajo

    ligero Dispatchers.IO Lejos del Main Thread, trabajo de network or disk Dispatchers.Default Lejos de Main Thread, trabajo en el CPU
  29. Coroutines launch (Dispatcher.IO) { userRemoteDataSource.getUser() withContext(Dispatchers.Main) { titleUserName?.text = user?.name

    } }
  30. COROUTINES EN TODOS LADOS Network Request and Retrofit, Room Workers,

    Live Data, viewModelScope & Unit testing
  31. NETWORK REQUEST & RETROFIT

  32. Medium Blog: https://medium.com/ knowing-android

  33. Coroutines network request We get our result asynchronous and can

    be: We show the result to the main thread We make a call We create a coroutine scope await() Success<T> withContext{ } launch{ }
  34. Network Request var user = userRemoteDataSource.getUser() showUserName(user?.name)

  35. Network Request fun getUserName() { GlobalScope.launch { var user =

    userRemoteDataSource.getUser() showUserName(user?.name) } }
  36. Network Request class UserRemoteDataSource { suspend fun getUser(): User {

    return retrofit.getUserName().await() } }
  37. Network Request class UserRemoteDataSource { suspend fun getUser(): ”User" {

    return retrofit.getUserName().await() } }
  38. Network Request @GET(“v1/someFancyUrl") fun getUserName(): Deferred<Response<ResponseBody>> Retrofit.Builder() .baseUrl(baseUrl) .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create())

    .addCallAdapterFactory(CoroutineCallAdapterFactory()) .build()
  39. Network Request @GET(“v1/someFancyUrl") suspend fun getUserName(): Response<ResponseBody> Retrofit 2.6.0

  40. Network Request fun getUserName() { GlobalScope.launch { var user =

    userRemoteDataSource.getUser() showUserName(user?.name) } }
  41. Network Request fun getUserName() { GlobalScope.launch { var user =

    userRemoteDataSource.getUser() withContext(Dispatchers.Main) { showUserName(user?.name) } } }
  42. ROOM & COROUTINES

  43. Room @Query("SELECT * from User LIMIT 1") fun getUser(): UserEntity

  44. Room @Query("SELECT * from User LIMIT 1") open suspend fun

    getUser(): UserEntity
  45. Room @Insert suspend fun insertUser(user: UserEntity)

  46. Room @Transaction open suspend fun saveUser (user: UserEntity) { insertUser(user)

    insertToken(user) }
  47. Room suspend fun updateUser(){ database.withTransaction { database.userDao().updateUser() database.tokenDao().updateToken() } }

  48. Room @Insert suspend fun insertUser(user: UserEntity)

  49. Room @Insert suspend fun insertUser(user: UserEntity) @Override public Object insertUserSuspend(final

    UserEntity user,final Continuation<?super Unit> p1){ return CoroutinesRoom.execute(__db, new Callable<Unit>() { @Override public Unit call() throws Exception { __db.beginTransaction(); try { __insertionAdapterOfUser.insert(user); __db.setTransactionSuccessful(); return kotlin.Unit.INSTANCE; } finally { __db.endTransaction(); } } }, p1); } Room 2.2.0
  50. WORKER’S & COROUTINES

  51. Workers val myWorker = PeriodicWorkRequestBuilder<MyWorker>(WORKER_POLICY, TimeUnit.MINUTES) .build()

  52. Workers val myWorker = PeriodicWorkRequestBuilder<MyWorker>(WORKER_POLICY, TimeUnit.MINUTES) .addTag(MyWorker.JOB_NAME) .build()

  53. Workers val myWorker = PeriodicWorkRequestBuilder<MyWorker>(WORKER_POLICY, TimeUnit.MINUTES) .setConstraints(getConstraints()) .addTag(MyWorker.JOB_NAME) .build()

  54. Workers val myWorker = PeriodicWorkRequestBuilder<MyWorker>(WORKER_POLICY, TimeUnit.MINUTES) .setConstraints(getConstraints()) .addTag(MyWorker.JOB_NAME) .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, BACKOFF_POLICY,

    TimeUnit.MINUTES) .build()
  55. Workers class MyWorker (ctx: Context, params: WorkerParameters): Worker(ctx, params) {

    override fun doWork(): Result { return try { userRemoteDataSource.syncBookmarks() userLocalDataSource.updateUser() Result.success() } catch (ex: Exception) { Result.failure() } } }
  56. Workers override fun doWork(): Result { userRemoteDataSource.syncBookmarks() userLocalDataSource.updateUser() return Result.success()

    }
  57. Workers override fun doWork(): Result { userRemoteDataSource.syncBookmarks() if(isStoped()) return Result.failure()

    userLocalDataSource.updateUser() if(isStoped()) return Result.failure() return Result.success() }
  58. Workers class MyWorker (ctx: Context, params: WorkerParameters) : CoroutineWorker(context, params)

    { override fun doWork(): Result = withContext(Dispatchers.IO) { userRemoteDataSource.syncBookmarks() userLocalDataSource.updateUser() return Result.success() } } Work-Runtime 2.0.0
  59. LIVE DATA OBJECTS & COROUTINES

  60. LiveData val user: LiveData<User> = liveData { val data =

    userRemoteDataSource.getUser() emit(data) }
  61. LiveData val user: LiveData<Result> = liveData { emit(Result.loading()) try {

    emit(Result.success(fetchUser()) } catch(ioException: Exception) { emit(Result.error(ioException)) } }
  62. VIEWMODEL SCOPE, & COROUTINES LEAKS

  63. ViewModel ViewModel se encarga de almacenar y manejar la información

    relacionada con el UI tomando en cuenta el ciclo de vida de la aplicación
  64. Coroutines Leak class MainViewModel : ViewModel() { private val viewModelJob

    = SupervisorJob() val scope = CoroutineScope(Dispatchers.IO + viewModelJob) fun getUser() = scope.launch { //Do some suspendable and ultra hardcore code } }
  65. Coroutines Leak class MainViewModel : ViewModel() { private val viewModelJob

    = SupervisorJob() val scope = CoroutineScope(Dispatchers.IO + viewModelJob) fun getUser() = scope.launch { //Do some suspendable and ultra hardcore code } override fun onCleared() { super.onCleared() viewModelJob.cancel() } }
  66. viewModelScope class MainViewModel : ViewModel() { fun getUser() = viewModelScope.launch

    { //Do some suspendable code } }
  67. TEST ALL THE COROUTINES

  68. Unit testing and Coroutines @Before fun before() { MockitoAnnotations.initMocks(this) Dispatchers.setMain(Dispatchers.Unconfined)

    } @Test fun createFirstTimeTrue() { runBlocking { val user = userLocalDataSource.getUser() verify(user).`is`.yourUser } }
  69. Unit testing and Room @Test fun insertAndGetUser() = runBlocking {

    userDao.insertUser(user) val userAvailable = userDao.getUser() assertEquals(user, userAvailable) }
  70. Unit testing and Room @RunWith(JUnit4::class) class MyWorkTest { @Test fun

    testMyWork() { val worker = TestListenableWorkerBuilder<MyWorker>(mockContext).build() val result = worker.startWork().get() assertThat(result, `is`(Result.success())) } }
  71. Gracias Medium: https://medium.com/knowing-android Twitter: @ddinorahtovar