Clean, Easy & New Android Arch. : Devoxx-BE 17

3142db3adb711e247e371153b5777e04?s=47 Britt Barak
November 09, 2017

Clean, Easy & New Android Arch. : Devoxx-BE 17

3142db3adb711e247e371153b5777e04?s=128

Britt Barak

November 09, 2017
Tweet

Transcript

  1. Clean, Easy & New Android Architecture Britt Barak Devoxx Belgium

    2017
  2. Britt Barak @brittBarak Android Developer & TL Android Academy Women

    Techmakers
  3. And you...?

  4. One thing is sure Everything will change. Quick.

  5. My First Startup

  6. “As You Like It” / W. Shakespeare ״All the world’s

    a stage, And all the men and women merely players; They have their exits and their entrances, And one man in his time plays many parts:״
  7. Players Exits and Entrances Plays a part → Single responsibility

    → Defined interfaces → Separation of concerns
  8. Our Goals - Write quicker - Change easily - Rely

    on the code (test) - Easy for others to understand
  9. App Architecture with the new components

  10. Who loves Jelly Beans?

  11. None
  12. None
  13. None
  14. None
  15. Where to start?

  16. Presentation Layer Data Layer Domain Layer

  17. None
  18. Is about: UI Is not about: logic. Presentation Layer

  19. class JellyBeanViewModel extends ViewModel { String flavor; int r; int

    g; int b;
  20. @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_add_bean); setViewModel()

    setUi(); }
  21. void setViewModel(){ this.viewModel = new JellyBeanViewModel(); }

  22. UI represents the ViewModel state

  23. Presentation Layer Domain Layer View ViewModel User Input

  24. 1. Update the viewModel

  25. public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { switch

    (seekBar.getId()) { case R.id.sb_r: viewModel.setR(progress); break; //...
  26. 2. View Model notifies

  27. - Callbacks - Data binding - RxJava - LiveData How

    to notify?
  28. LiveData - Observable data holder class - Lifecycle-aware - Updates

    only on started / resumed state - Removes itself on destroyed state - No memory leaks https://developer.android.com/topic/libraries/architecture/livedata.html
  29. public class JellyBeanViewModel extends ViewModel { String flavor; LiveData<Integer> r;

    LiveData<Integer> g; LiveData<Integer> b;
  30. public class JellyBeanViewModel extends ViewModel { LiveData<Integer> r; public void

    setR(int r) { r = validate(r); this.r.setValue(r); }
  31. 3. Create an observer

  32. Observer<Integer> colorChangedObserver = (Integer integer) -> { beanIv.setColorFilter(viewModel.getColor()); };

  33. 4. Observe ViewModel changes

  34. viewModel.getR() .observe(this, colorChangedObserver); Observer is tied to this lifecycle, and

    will be removed as the lifecycle ends. LivaData
  35. Presentation Layer Domain Layer ViewModel View set() User Input notify

  36. Persisting ViewModel

  37. None
  38. setViewModel() -> { this.viewModel = ViewModelProviders.of(this) .get(JellyBeanViewModel.class); }

  39. And for more complex ViewModels?

  40. 1. Multiple LiveData Objects 2. Separate class → Lifecycle Aware

    Suggestions: Peach Dream Peach Pie P
  41. Save The Jelly Bean!

  42. Is about: “objective” data Is not about: caring about the

    consumer Data Layer
  43. - Data Model - Repository Data Layer

  44. class JellyBeanViewModel String flavor; int r; int g; int b;

    class JellyBean String flavor; String color;
  45. - One per data type (e.g jelly bean, recipe, user….).

    - Encapsulates the logic of getting/setting the data. - CRUD operations (Create, Read, Update, Delete) Repository
  46. class JellyBeanRepo{ }

  47. class JellyBeanRepo{ LiveData<JellyBean> getJellyBean(String id){ }

  48. LiveData<JellyBean> getJellyBean(String id) { return myFirebaseClient.getJellyBean(id) }

  49. LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ return cache.getJellyBean(id); } else{

    return myFirebaseClient.getJellyBean(id) } }
  50. LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ return cache.getJellyBean(id); } else

    if (appDatabase.hasJellyBean(id)) { return appDatabase.jellyBeanDao().getJellyBean(id); } else{ return myFirebaseClient.getJellyBean(id) }
  51. LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ return cache.getJellyBean(id); } else

    if (appDatabase.hasJellyBean(id)) { return appDatabase.jellyBeanDao().getJellyBean(id); } else{ return myApiClient.getJellyBean(id) }
  52. Presentation Layer Data Layer ViewModel DataModel ?

  53. Presentation Layer Data Layer Domain Layer DataModel Interactor ViewModel

  54. Is about: converting models Is not about: UI, Data Domain

    Layer
  55. Save The Jelly Bean!

  56. 1. Create Use Case

  57. class SaveJellyBean extends UseCase { void execute() { }

  58. 2. Convert Models

  59. public class SaveJellyBean extends UseCase { public void execute(JellyBeanViewModel viewModel)

    { JellyBean data = prepareDataModel(viewModel); }
  60. JellyBean prepareDataModel(JellyBeanViewModel viewModel){ String beanColorString = String.format("#%06X", (0xFFFFFF & viewModel.getColor()));

    JellyBean data = new JellyBean(viewModel.getFlavor(), beanColorString); return data; }
  61. 3. Call Repository

  62. public class SaveJellyBean extends UseCase { public void execute(JellyBeanViewModel viewModel)

    { JellyBean data = prepareDataModel(viewModel); repo.saveBean(data); }
  63. 4. Execute

  64. Presentation Layer Data Layer Domain Layer DataModel Interactor ViewModel

  65. Presentation Layer Data Layer Domain Layer SaveJellyBean .execute() JellyBeanRepo .save()

  66. Reusing interactors - Edit Jelly Bean screen - Different UI

    - Same SaveJellyBean use case
  67. Some more about Repository...

  68. Data Layer Network Service Local DB Cache? Domain Layer Some

    Usecase
  69. Local database - Persistence between sessions - Single source of

    truth
  70. Room - Wraps SQLite database - Generates boilerplate code -

    Verifies queries at compile time - Denies calls on the UI thread
  71. 1. Create Entity

  72. @Entity public class JellyBean { @PrimaryKey @NonNull String id; String

    flavor; String color;
  73. 2. Create Dao

  74. @Dao public interface JellyBeanDao { @Query("select * from jellyBean") LiveData<List<JellyBean>>

    getAllJellyBeans(); @Query("select * from jellyBean where id = :beanId") LiveData<JellyBean> getJellyBean(String beanId); }
  75. @Insert(onConflict = REPLACE) void insertJellyBean(JellyBean jellyBean); @Delete void deleteJellyBean(JellyBean jellyBean);

  76. 3. Create App Database instance

  77. @Database(version = 1, entities = {JellyBean.class}) public abstract class AppDatabase

    extends RoomDatabase { private static AppDatabase instance; public abstract JellyBeanDao jellyBeanDao(); //init and destroy code.. }
  78. public static AppDatabase getInMemoryDatabase(Context appContext){ if (instance == null) {

    instance = Room.inMemoryDatabaseBuilder( appContext, AppDatabase.class).build(); } return instance; } public static void destroyInstance() { instance = null; }
  79. 4. Use from Repository

  80. public LiveData<JellyBean> getJellyBean(String id) { if (cache.hasJellyBean(id)){ //… return appDatabase.jellyBeanDao().getJellyBean(id);

    //... }
  81. Presentation Layer Data Layer Domain Layer SaveJellyBean .execute() JellyBeanRepo .save()

    LocalDB JellyBeanDao
  82. Presentation Layer • View • ViewModel • Presenter Data Layer

    • Repository • DataModel Domain Layer • Interactor • Use case
  83. Players Exits and Entrances Plays a part → Single responsibility

    → Defined interfaces → Separation of concerns
  84. Our Goals - Write quicker - Change easily - Rely

    on the code (test) - Easy for others to understand
  85. Get my notes on - @britt.barak Keep in touch! Britt

    Barak @brittBarak Thank you!
  86. Thank You !