ViewModel: A deep dive (Mobile Optimized Minsk)

ViewModel: A deep dive (Mobile Optimized Minsk)

At Google I/O we got the Android architecture components.
One of its most interesting concepts is the new ViewModel.
How does it work? How do I plug it into my app? Can I use it without any of the other components?
Can I plug it into an existing MVVM or how would I build MVVM with Google's ViewModel?

A8b79d304b5184e5a5b0a109590f6683?s=128

Danny Preussler

July 15, 2017
Tweet

Transcript

  1. The ViewModel @PreusslerBerlin a deep dive Danny Preussler

  2. None
  3. The ”architecture components” ViewModel like in Model-View-ViewModel ViewModel?

  4. ViewModel in MVVM world

  5. ViewModel in data binding <TextView … android:id="@+id/all_shows_item_title" android:text="@{viewModel.title}" /> <android.support.v7.widget.CardView

    … android:onClick="@{() -> viewModel.onClicked()}"> <data> <variable name="viewModel" type="com.vmn.playplex.main.allshows.SeriesViewModel"/> </data>
  6. in new suggested architecture

  7. Is MVP dead?

  8. Is MVP dead? It’s like Java and Kotlin: •MVP will

    stay for quite some time •There is a new cooler kid in town that won’t leave Put the ViewModel behind Presenter
  9. Is MVP dead? It’s like Java and Kotlin: •MVP will

    stay for quite some time •There is a new cooler kid in town that won’t leave Put the ViewModel behind Presenter
  10. Is MVP dead? It’s like Java and Kotlin: •MVP will

    stay for quite some time •There is a new cooler kid in town that won’t leave Put the ViewModel behind Presenter
  11. Is MVP dead? It’s like Java and Kotlin: •MVP will

    stay for quite some time •There is a new cooler kid in town that won’t leave Put the ViewModel behind Presenter
  12. In architecture components •A ViewModel provides the data for a

    specific UI •The ViewModel does not know about the View! •Survives configuration change
  13. In architecture components •A ViewModel provides the data for a

    specific UI •The ViewModel does not know about the View! •Survives configuration change
  14. In architecture components •A ViewModel provides the data for a

    specific UI •The ViewModel does not know about the View! •Survives configuration change
  15. In architecture components •Remember configuration change can be: •Rotation •Any

    other resize i.e. split screen •Language change
  16. life cycle: rotation onCreate onStart onResume onPause onStop onDestroy onCreate

    onStart onResume ViewModel
  17. life cycle: finish onCreate onStart onResume onPause onStop onDestroy ViewModel

  18. How to use compile 'android.arch.lifecycle:runtime:1.0.0-alpha4' compile 'android.arch.lifecycle:extensions:1.0.0-alpha4’ // annotationProcessor //

    'android.arch.lifecycle:compiler:1.0.0-alpha4'
  19. How to use class MyViewModel extends ViewModel {

  20. How to use class MyViewModel extends AndroidViewModel {

  21. How to use class MyViewModel extends ObservableViewModel { Coming soon

    Jose Alcérreca, Google https://medium.com/@dpreussler/add-the-new-viewmodel-to-your-mvvm-36bfea86b159
  22. How to use public void onCreate(...) { model = ViewModelProviders

    .of(this) .get(MyViewModel.class); }
  23. What if… constructor arguments needed?

  24. How to use class MyViewModelFactory implements ViewModelProvider.Factory { @Inject MyUseCase

    useCase; @Override public MyViewModel create(Class modelClass) { return new MyViewModel(useCase); } }
  25. How to use ViewModelProviders .of(this, new MyViewModelFactory()) .get(MyShowsViewModel.class);

  26. How to use •Always try to build your own Factory

    •Default factory uses newInstance() which is some hundred times slower than new calls (reflection) https://speakerdeck.com/dpreussler/comparing-dependency-injection- frameworks-and-internals-for-android •Don’t forward life cycle events!
  27. How to use •Always try to build your own Factory

    •Default factory uses newInstance() which is some hundred times slower than new calls (reflection) https://speakerdeck.com/dpreussler/comparing-dependency-injection- frameworks-and-internals-for-android •Don’t forward life cycle events!
  28. How to use •Always try to build your own Factory

    •Default factory uses newInstance() which is some hundred times slower than new calls (reflection) https://speakerdeck.com/dpreussler/comparing-dependency-injection- frameworks-and-internals-for-android •Don’t forward life cycle events!
  29. What if… I need to clean something when destroyed?

  30. What if… class MyViewModel extends ViewModel { @Override protected void

    onCleared() { super.onCleared(); cleanupSubscriptions(); }
  31. How does it survive orientation change?

  32. How does it actually work? class HolderFragment extends Fragment {

    public HolderFragment() { setRetainInstance(true); } …
  33. How does it actually work? String HOLDER_TAG = "android.arch.lifecycle.state.StateProviderH olderFragment";

  34. How does it know the activity is finishing?

  35. How does it actually work? @Override public void onDestroy() {

    super.onDestroy(); mViewModelStore.clear(); }
  36. Can I do it differently? ViewModel is not life cycle

    aware?
  37. It just refuses to die

  38. remember Never hold View or Activity references in the ViewModel!

  39. Could you Write that?

  40. Could you write that? •What if asked for ViewModel but

    fragment transaction not done yet? •You might end up with duplicates
  41. Could you write that? •What if asked for ViewModel but

    fragment transaction not done yet? •You might end up with duplicates
  42. Could you write that? static class HolderFragmentManager { private Map<Activity,

    HolderFragment> mNotCommittedActivityHolders = new HashMap<>(); private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>(); private ActivityLifecycleCallbacks mActivityCallbacks = new EmptyActivityLifecycleCallbacks() { @Override public void onActivityDestroyed(Activity activity) { HolderFragment fragment = mNotCommittedActivityHolders.remove(activity); …
  43. What if Two Fragments of same Activity ask for same

    ViewModel.class via ViewModelProviders .of(this) .get(MyViewModel.class);
  44. result Different ViewModels

  45. What if Two Fragments of same Activity ask for same

    ViewModel.class via ViewModelProviders .of(this) .get(MyViewModel.class);
  46. result •Fragment and Activity share the same FragmentManager but: •Implementation

    uses Activity’s FragmentManager but ChildFragmentManager for Fragments
  47. What if Two Fragments of same Activity ask for same

    ViewModel.class via ViewModelProviders .of(getActivity()) .get(MyViewModel.class);
  48. result Same ViewModel

  49. Other uses cases communication layer between activities and fragments or

    fragments and fragments
  50. Does that mean all problems are solved?

  51. all problems solved? ViewModels provide a convenient way to retain

    data across configuration changes but they are not persisted if the application is killed by the operating system https://developer.android.com/topic/libraries/architecture/viewmodel.html#viewm odel_vs_savedinstancestate
  52. but but WHY?

  53. Why? The data saved via onSaveInstanceState is kept in the

    system process memory and the Android OS allows you to keep only a very small amount of data so it is not a good place to keep actual data for your app. TransactionTooLargeException anyone?
  54. MeaNS ViewModels gives us rotation But takes away recreation

  55. after The truth •Keep non-UI states in non-UI layer Not

    in bundle! •Use real caching strategies •Allows updating cache in background
  56. after The truth •Keep non-UI states in non-UI layer Not

    in bundle! •Use real caching strategies •Allows updating cache in background
  57. after The truth •Keep non-UI states in non-UI layer Not

    in bundle! •Use real caching strategies •Allows updating cache in background
  58. but but EditText might have restored it’s state but the

    ViewModel will not now about it
  59. but but Where to store the UI state?

  60. store the UI state In Bundles!

  61. but but Who owns the UI state?

  62. store the UI state The ViewModel

  63. store the UI state

  64. lets tweak it class MyModelFactory implements ViewModelProvider.Factory { … public

    MyModelFactory(Bundle bundle) { this.bundle = bundle; } @Override public MyViewModel create(Class modelClass) { MyViewModel viewModel = new MyViewModel(); viewModel.readFrom(bundle); return viewModel; }
  65. lets tweak it @Override public void onSaveInstanceState(Bundle bundle){ super.onSaveInstanceState(bundle); viewModel.writeTo(bundle);

    }
  66. lets tweak it class BundleAwareFactory<T extends BundleableViewModel> implements ViewModelProvider.Factory {

    Bundle bundle; ViewModelProvider.Factory provider; public BundleAwareViewModelFactory( @Nullable Bundle bundle, ViewModelProvider.Factory provider) { this.bundle = bundle; this.provider = provider; }
  67. lets tweak it ... @Override public T create(final Class modelClass)

    { T viewModel = (T) provider.create(modelClass); if (bundle != null) { viewModel.readFrom(bundle); } return viewModel; }
  68. lets tweak it public abstract class BundleableViewModel extends ViewModel {

    abstract void writeTo(Bundle bundle); abstract void readFrom(Bundle bundle); }
  69. What if Activity needs multiple version of same ViewModel.class?

  70. Extend IT so we can have unique class names ?!

  71. But what if count is dynamic ?

  72. SO unknown size of elements ?

  73. CALLED RecyclerVIEW!

  74. list item ViewModels In MVVM: •each item in RecyclerView should

    be backed by a ViewModel •ListViewModel and ItemViewModels •ItemViewModels normally don’t retrieve their own state so no need to extend Googles ViewModel
  75. list item ViewModels In MVVM: •each item in RecyclerView should

    be backed by a ViewModel •ListViewModel and ItemViewModels •ItemViewModels normally don’t retrieve their own state so no need to extend Googles ViewModel
  76. list item ViewModels In MVVM: •each item in RecyclerView should

    be backed by a ViewModel •ListViewModel and ItemViewModels •ItemViewModels normally don’t retrieve their own state so no need to extend Googles ViewModel
  77. Let’s do it anyway

  78. list item ViewModels public <T extends ViewModel> T get( Class<T>

    modelClass)
  79. list item ViewModels •Key could be position? public <T extends

    ViewModel> T get( String key, Class<T> modelClass)
  80. What about Changes?

  81. What about Removal?

  82. list item ViewModels “ViewModelProvider, which will create ViewModels via the

    given Factory and retain them in a ViewModelStore “
  83. public class ViewModelStore { private final HashMap<String, ViewModel> mMap =

    new HashMap<>(); final void put(String key, ViewModel viewModel) {…} final ViewModel get(String key) { …} public final void clear() {…} }
  84. public class ViewModelStore { private final HashMap<String, ViewModel> mMap =

    new HashMap<>(); final void put(String key, ViewModel viewModel) {…} final ViewModel get(String key) { …} public final void clear() {…} }
  85. Store can not be overridden

  86. list item ViewModels RecyclerView items and Googles ViewModel don’t fit

    together
  87. What if Two different activities need same ViewModel.class?

  88. This Does not sound like MVVM

  89. Let’s do it anyway

  90. Can I do it differently? •Let’s assume we build transactional

    flow spreading over multiple Activities and wand to keep data in ViewModel •Can not use the retained fragment approach •Need to override how the ViewModel is stored •Need to override the ViewModelStore is stored
  91. Viewmodelstoreowner “A responsibility of an implementation of this interface is

    to retain owned ViewModelStore during the configuration changes and call ViewModelStore#clear(), when this scope is going to be destroyed.“
  92. Viewmodelstoreowner public interface ViewModelStoreOwner { ViewModelStore getViewModelStore(); }

  93. Viewmodelproviders /** * Utilities methods for {@link ViewModelStore} class. */

    public class ViewModelProviders {
  94. ViewModelproviders public static ViewModelProvider of( Fragment fragment, Factory factory) {

    return new ViewModelProvider( ViewModelStores.of(fragment), factory); }
  95. Viewmodelstores /** * Factory methods for ViewModelStore class. */ public

    class ViewModelStores {
  96. ViewModelstores ViewModelStore of(Fragment fragment) { return holderFragmentFor(fragment) .getViewModelStore(); }

  97. lets do it new ViewModelProvider( new LongLivingViewModelStoreOwner(), new BundleAwareViewModelFactory (bundle,

    provider)) .get(MyViewModel.class);
  98. How to extend life? Toothpick: A scope tree based Dependency

    Injection (DI) library https://github.com/stephanenicolas/toothpick
  99. lets do it class LongLivingViewModelStoreOwner implements ViewModelStoreOwner { @Override public

    ViewModelStore getViewModelStore() { return Toothpick.openScope(SCOPE_NAME) .getInstance(ViewModelStore.class); } public void discardViewModel() { Toothpick.closeScope(SCOPE_NAME); }
  100. let`s sum up •Well designed API •Know the life time:

    Never have Activivity or View reference in ViewModel •Be aware of Recreation •Careful with RecyclerView •Still alpha
  101. Want to know more •https://medium.com/@dpreussler/add-the-new- viewmodel-to-your-mvvm-36bfea86b159 •https://proandroiddev.com/customizing-the-new- viewmodel-cf28b8a7c5fc •http://hannesdorfmann.com/android/arch- components-purist

    •https://www.techyourchance.com/android- viewmodel-architecture-component-harmful/
  102. Southpark copyright Disclaimer

  103. viacom.tech/link/mobileoptimized/2017/careers We are hiring

  104. The ViewModel @PreusslerBerlin a deep dive Danny Preussler