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

Android Async Talk

Robin Caroff
November 15, 2018

Android Async Talk

Most of the developers (including me of course 😅) struggle with asynchronous programming and tasks scheduling. Android developers are no exception to this!

In this talk we'll have a look at the different tools offered by the Android native programming languages (Java / Kotlin), the Android SDK, and the community (e.g.: HAMER framework, AsyncTask, Coroutines, RxJava, WorkManager, etc.) to perform asynchronous operations, schedule tasks, handle errors and deal with many of the Android OS challenges.

We’ll see pros and cons of those tools and we'll try to find an elegant, modern and efficient approach to suit our needs.

Robin Caroff

November 15, 2018
Tweet

More Decks by Robin Caroff

Other Decks in Programming

Transcript

  1. The Async Talk An overview of asynchronous programming for the

    Android platform Robin Caroff @RobinCaroff
  2. Loaders Pros • Run on seperate threads • Simplify thread

    management with callbacks • Persist and cache results across configuration changes • Can implement an observer to monitor for changes in the underlying data source Cons • Retains reference to the activity • Implementation is not great, many callbacks • Made to manage UI/background tasks
  3. Services A Service is an application component that can perform

    long-running operations in the background, and it does not provide a user interface.
  4. Scheduled Jobs • Job Scheduler • Firebase Job Dispatcher •

    Alarm Manager • Job Intent Service • Work Manager
  5. Work Manager WorkManager aims to simplify the developer experience by

    providing a first-class API for system-driven background processing. It is intended for background jobs that should run even if the app is no longer in the foreground. Where possible, it uses JobScheduler or Firebase JobDispatcher to do the work; if your app is in the foreground, it will even try to do the work directly in your process.
  6. Rx Java/Kotlin - Rx Android • Avoid the callback hell

    • Simple Error Handling Tool • Easy Multi-Threading • Chain jobs
  7. Rx Java/Kotlin - Rx Android userDao.getAllUsers() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<RealmResults<User>>()

    { @Override public void onError(Throwable e) { // Handle errors here } @Override public void onNext(RealmResults<User> users) { // You can access your Users } @Override public void onCompleted() { // All your user objects have been fetched. Done! } });
  8. Rx Java/Kotlin - Rx Android userDao.getAllUsers() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( users

    -> { /* Refresh UI with your new users */ }, error -> { /* Handle errors here */ }, () -> { /* All user received, remove loading indicator */} );
  9. Rx Java/Kotlin - Rx Android userDao.getAllUsers() .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(this::longTransformation) .doOnError(error

    -> { /* Log transformation error */ }) .onErrorResumeNext(error -> transformedUsersBackup()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( transformedUsers -> { /* Refresh UI with your new transformed users */ }, error -> { /* Handle errors here */ }, () -> { /* All user received and transformed, remove loading indicator */} );
  10. Rx Java/Kotlin - Rx Android userDao.getAllUsers() .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(this::longTransformation) .doOnError(error

    -> { /* Log transformation error */ }) .onErrorResumeNext(error -> transformedUsersBackup()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( transformedUsers -> { /* Refresh UI with your new transformed users */ }, error -> { /* Handle errors here */ }, () -> { /* All user received and transformed, remove loading indicator */} );
  11. Rx Java/Kotlin - Rx Android userDao.getAllUsers() .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(this::longTransformation) .doOnError(error

    -> { /* Log transformation error */ }) .onErrorResumeNext(error -> transformedUsersBackup()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( transformedUsers -> { /* Refresh UI with your new transformed users */ }, error -> { /* Handle errors here */ }, () -> { /* All user received and transformed, remove loading indicator */} );
  12. Rx Java/Kotlin - Rx Android userDao.getAllUsers() .subscribeOn(Schedulers.io()) .observeOn(Schedulers.computation()) .map(this::longTransformation) .doOnError(error

    -> { /* Log transformation error */ }) .onErrorResumeNext(error -> transformedUsersBackup()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( transformedUsers -> { /* Refresh UI with your new transformed users */ }, error -> { /* Handle errors here */ }, () -> { /* All user received and transformed, remove loading indicator */} );
  13. Schedulers • schedulers.io() -> short execution (network/DB call) • Schedulers.computation()

    • Schedulers.newThread() • Schedulers.single() • Schedulers.from(Executor executor) • AndroidSchedulers.mainThread() • …
  14. Schedulers • Schedulers.io() -> short execution (network/DB call) • Schedulers.computation()

    -> long/CPU-intensive tasks • Schedulers.newThread() • Schedulers.single() • Schedulers.from(Executor executor) • AndroidSchedulers.mainThread() • …
  15. Schedulers • Schedulers.io() -> short execution (network/DB call) • Schedulers.computation()

    -> long/CPU-intensive task • Schedulers.newThread() -> long running, isolated task • Schedulers.single() • Schedulers.from(Executor executor) • AndroidSchedulers.mainThread() • …
  16. Schedulers • Schedulers.io() • Schedulers.computation() • Schedulers.newThread() -> long running,

    isolated task • Schedulers.single() -> sequential tasks • Schedulers.from(Executor executor) • AndroidSchedulers.mainThread() • …
  17. Schedulers • Schedulers.io() -> short execution (network/DB call) • Schedulers.computation()

    -> long/CPU-intensive tasks • Schedulers.newThread() -> long running, isolated task • Schedulers.single() -> sequential tasks • Schedulers.from(Executor executor) -> custom threading logic • AndroidSchedulers.mainThread() • …
  18. Schedulers • Schedulers.io() -> short execution (network/DB call) • Schedulers.computation()

    -> long/CPU-intensive tasks • Schedulers.newThread() -> long running, isolated task • Schedulers.single() -> sequential tasks • Schedulers.from(Executor executor) -> custom threading logic • AndroidSchedulers.mainThread() -> Android main/UI thread • …
  19. Kotlin’s Coroutines • Coroutines are like very light-weight threads •

    Threads are used to run coroutines but one thread can execute thousands of coroutines • Coroutines can be suspended at specified suspension points • Also avoid callbacks hell and allow exceptions to be used. Write asynchronous code in continuous style • Coroutines are no longer an experimental feature
  20. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } }
  21. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } }
  22. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } }
  23. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } }
  24. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } }
  25. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } } suspend fun getAllUsers(): List<User> { // Async call }
  26. Kotlin’s Coroutines val lifecycleAwareJob = Job() val uiScope = CoroutineScope(Dispatchers.Main

    + lifecycleAwareJob) uiScope.launch { try { val transformedUsers = userDao.getAllUsers() .map { it -> heavyTransformation(it) } updateUIWithUsers(transformedUsers) } catch (e: UserDaoException) { // Handle errors here } }
  27. Kotlin’s Coroutines • Language: • async / await • few

    primitives • Library (kotlinx-coroutines): • launch • runBlocking • future • etc.
  28. Take Away • Memory management • Let Android know you

    intentions • Separation of concerns • New tools are coming to us as restrictions are growing. Work Manager is conjunction with Kotlin’s coroutines looks very promising