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

Refactoring Wunderlist for Android - Episode I. The presentation layer -

Refactoring Wunderlist for Android - Episode I. The presentation layer -

In this talk, we will go through an overview of how Wunderlist was built and how we are now refactoring the code base to make it better, more decoupled, more testable, easier to extend, and easier to maintain.

In this 1st chapter of refactoring Wunderlist, we will go through the MVP pattern used to decouple the different elements of the view, and I will show also the different approaches we have taken for fetching data, till the one we've finally chosen, using reactive programming.

Cesar Valiente

April 16, 2016
Tweet

More Decks by Cesar Valiente

Other Decks in Technology

Transcript

  1. Who is this guy? Image Placeholder César Valiente Android Engineer

    @Wunderlist (@Microsoft) Android Google Developer Expert (GDE) +CesarValiente @CesarValiente
  2. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project
  3. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project Network Layer Network layer (Accessing to the API data) Java project
  4. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project Network Layer Network layer (Accessing to the API data) Java project Sync boundaries
  5. How is Wunderlist built? Android Layer Presentation layer (UI and

    Android stuff) Android project Sync Layer Model layer (Business logic) Java project Network Layer Network layer (Accessing to the API data) Java project Sync boundaries Network boundaries
  6. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Database

    in presentation layer (since it was our only Android related layer).
  7. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer).
  8. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer). Cache in the sync layer.
  9. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer). Cache in the sync layer. Complex data schemes used also in the presentation layer, not needed here.
  10. Problems? Activities/Fragments become GOD classes. Responsibilities are messed up. Difficult

    to test: framework dependencies. Database in presentation layer (since it was our only Android related layer). Cache in the sync layer. Complex data schemes used also in the presentation layer, not needed here. We wanted to do it better!
  11. What are we going to do? We are going to

    separate responsibilities. Yes, Even more!
  12. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app.
  13. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app. We are going to improve the way we fetch data.
  14. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app. We are going to improve the way we fetch data. We are going to make testing easier to do, and increase our test coverage.
  15. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to decouple the different core elements of our app. We are going to improve the way we fetch data. We are going to make testing easier to do, and increase our test coverage. We are going to improve our codebase.
  16. What are we going to do? We are going to

    separate responsibilities. Yes, Even more! We are going to REFACTOR We are going to decouple the different core elements of our app. We are going to improve the way we fetch data. We are going to make testing easier to do, and increase our test coverage. We are going to improve our codebase.
  17. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Everything starts with the Model View Presenter (MVP)
  18. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    User interacts with the app Everything starts with the Model View Presenter (MVP)
  19. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process User interacts with the app Everything starts with the Model View Presenter (MVP)
  20. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process Executes use case User interacts with the app Everything starts with the Model View Presenter (MVP)
  21. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process Executes use case Returns the result User interacts with the app Everything starts with the Model View Presenter (MVP)
  22. VIEW Activity/Fragment PRESENTER Man in the middle MODEL Business logic

    Starts the process Executes use case Updates view Returns the result User interacts with the app Everything starts with the Model View Presenter (MVP)
  23. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter creates an instance and passes it to the presenter
  24. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter creates an instance and passes it to the presenter will execute it to start the process
  25. VIEW PRESENTER VIEWCALLBACK USE CASE implements and passes it to

    the presenter creates an instance and passes it to the presenter will invoke it to communicate back (when we have the result) will execute it to start the process
  26. private View progressDialog;
 private Adapter adapter;
 private SharingPresenter sharingPresenter;
 


    public void onCreate (Bundle onSavedInstanceState) {
 super.onCreate(onSavedInstanceState);
 
 bindViews();
 setupPresenters();
 } public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback {
  27. private View progressDialog;
 private Adapter adapter;
 private SharingPresenter sharingPresenter;
 


    public void onCreate (Bundle onSavedInstanceState) {
 super.onCreate(onSavedInstanceState);
 
 bindViews();
 setupPresenters();
 } public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { private void setupPresenters () {
 GetListMembershipUseCase getListMembershipUseCase = new GetListMembershipUseCase( listId, appDataController); sharingPresenter = new SharingPresenter ( this, getListMembershipUseCase);
 } //——————— next slides ———————//
  28. public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { //——————————-— previous

    slide (setup) ———————————-—// //——————— next slide (presenter invocation) ———————//
  29. @Override void showProgressDialog() {
 progressDialog.show();
 }
 
 @Override void hideProgressDialog()

    {
 progressDialog.hide();
 }
 
 @Override void updateList(List<Memberships> list) {
 adapter.updateContent(list);
 }
 } public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { //——————————-— previous slide (setup) ———————————-—// //——————— next slide (presenter invocation) ———————// implements ViewCallback {
  30. @OnClick private void showListMembers () {
 sharingPresenter.getListMembers();
 } //——————————— slide

    1 (setup) —————————-—// public class SharingFragmentActivity extends WLFragmentActivity implements ViewCallback { //————— slide 2 (ViewCallback impl) ————-//
  31. public class SharingPresenter extends Presenter { private ViewCallback viewCallback;
 private

    GetListMembershipUseCase getListMembershipUseCase;
 
 public SharingPresenter ( ViewCallback viewCallback, GetListMembershipUseCase getListMembershipUseCase) { 
 this.viewCallback = viewCallback;
 this.getListMembershipUseCase = getListMembershipUseCase;
 } //——————— next slides ———————//
  32. @Override public void onStart() {
 EventBus.getDefault().register(this);
 }
 
 @Override public

    void onStop() {
 EventBus.getDefault().unregister(this);
 } public class SharingPresenter extends Presenter { //——————————-— previous slide (setup) ———————————-—//
  33. @Override public void onStart() {
 EventBus.getDefault().register(this);
 }
 
 @Override public

    void onStop() {
 EventBus.getDefault().unregister(this);
 } public class SharingPresenter extends Presenter { //——————————-— previous slide (setup) ———————————-—// public void onEventMainThread(ListMembershipEvent event) {
 viewCallback.hideProgressDialog();
 viewCallback.updateList(event.getContent());
 } //——————— next slide (Use Case execution) ———————//
  34. public class SharingPresenter extends Presenter { //————————————- slide 1 (setup)

    ———————————————————-—// //———————————— slide 2 (listening for events) ———————//
  35. public void showListMembers () {
 viewCallback.showProgressDialog();
 getListMembershipUseCase.execute();
 }
 } public

    class SharingPresenter extends Presenter { //————————————- slide 1 (setup) ———————————————————-—// //———————————— slide 2 (listening for events) ———————//
  36. public class GetListMembershipUseCase implements UseCase<List<WLMembership>> { private String listId;
 private

    AppDataController appDataController;
 
 public GetListUseCase( String listId, AppDataController appDataController) {
 this.listId = listId;
 this.appDataController = appDataController;
 }
 

  37. public class GetListMembershipUseCase implements UseCase<List<WLMembership>> { private String listId;
 private

    AppDataController appDataController;
 
 public GetListUseCase( String listId, AppDataController appDataController) {
 this.listId = listId;
 this.appDataController = appDataController;
 }
 
 @Override
 public void execute() {
 appDataController.get(ApiObjectType.MEMBERSHIP, listId);
 }
 }
  38. LOADER Activity/ Fragment BUS Subscription Event or data is fired

    Presentation layer 2. Using loaders + Bus
  39. LOADER Activity/ Fragment BUS Subscription Event or data is fired

    Data Presentation layer 2. Using loaders + Bus
  40. LOADER Activity/ Fragment BUS Subscription Data Event or data is

    fired Data Presentation layer 2. Using loaders + Bus
  41. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e
  42. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1
  43. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2
  44. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3
  45. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3 4
  46. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 3 4
  47. 1. Use case that returns data. Synchronous. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 6 3 4
  48. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e
  49. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1
  50. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2
  51. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3
  52. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 3 4
  53. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 3 4
  54. 2. Use case using callback. Asynchronous. 2 Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 5 6 3 4
  55. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e BUS
  56. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 BUS
  57. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS
  58. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS 3
  59. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS 3 4
  60. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 BUS 3 4 5
  61. 3. Use case using an event bus. Presenter View Presentation

    layer Model UI domain db/ cache U s e C a s e 1 2 6 BUS 3 4 5
  62. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER (USE CASE)
  63. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 1 (USE CASE)
  64. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 1 2 (USE CASE)
  65. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 3 1 2 (USE CASE)
  66. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 3 4 1 2 (USE CASE)
  67. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer VIEW PRESENTER OBSERVER 5 3 4 1 2 (USE CASE)
  68. 4. Use case using RxJava Model UI domain db/ cache

    OBSERVABLE Presentation layer 6 VIEW PRESENTER OBSERVER 5 3 4 1 2 (USE CASE)
  69. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Dependency injection make testing easier
  70. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Use cases are very small and simple. Dependency injection make testing easier
  71. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Use cases are very small and simple. We can test now every component independently. Dependency injection make testing easier
  72. Testing? Views now don’t have business logic and/or data. Presenters

    don’t have framework dependencies. Use cases are very small and simple. We can test now every component independently. Dependency injection make testing easier Now testing is easy peasy!
  73. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava
  74. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava USE CASE TestSubscriber Mockito JUnit Java + RxJava
  75. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava USE CASE TestSubscriber Mockito JUnit Java + RxJava UI- DOMAIN TestSubscriber Mockito JUnit Java + RxJava
  76. More testing? VIEW Espresso Java + Android Mockito JUnit PRESENTER

    TestSubscriber Mockito JUnit Java + RxJava USE CASE TestSubscriber Mockito JUnit Java + RxJava UI- DOMAIN TestSubscriber Mockito JUnit Java + RxJava DB/ CACHE Java + Android + RxJava Robolectric Mockito JUnit TestSubscriber
  77. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build. Small use cases define what you want to do.
  78. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build. Small use cases define what you want to do. We have different alternatives to fetch data, choose yours.
  79. Lessons learned Dependency Inversion Principle is key. Following SOLID principles

    makes MVP easy to build. Small use cases define what you want to do. We have different alternatives to fetch data, choose yours. Decouple components makes testing easier.
  80. License (cc) 2016 César Valiente. Some rights reserved. This document

    is distributed under the Creative Commons Attribution-ShareAlike 3.0 license, available in http://creativecommons.org/licenses/by-sa/3.0/
  81. Image licenses Wunderlist (Microsoft): permission granted. Emojis by Emoji One

    (CC-BY): http://emojione.com/ Iceberg: http://science-all.com/iceberg.html All images belong to their owners.