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

ViewModels : Detrás de Escena

ViewModels : Detrás de Escena

Charla presentada junto a Fabio Negri (@Fabio_Negri) para la comunidad de Android Devs de Buenos Aires, en las oficinas de Accenture, como parte del cierre del año de la comunidad.
La presentación se basa en comprender cómo es que funcionan los ViewModels, cómo se logra mantener el estado de la información ante los cambios de configuración y cuáles son los errores más comunes a la hora de utilizar los Architecture Components.

Jorge Nicolás Nogueiras

December 04, 2019
Tweet

More Decks by Jorge Nicolás Nogueiras

Other Decks in Programming

Transcript

  1. ViewModels
 Detrás de escena Android Devs Buenos Aires Jorge Nogueiras

    @nogueirasj @nogueiras Fabio Negri @Fabio_Negri @fabionegri
  2. UN POCO DE HISTORIA… LOS COMPONENTES SON UN PUNTO DE

    PARTIDA. SI TU ARQUITECTURA YA FUNCIONA, NO ES NECESARIO QUE LA CAMBIES SavedStateHandle Coroutines viewModelScope ViewModel en NavGraph ViewModels en Databinding VAYAN MIGRANDO CUANTO ANTES A VIEWMODEL PORQUE SINO LES VA A CABER REFACTOR Codelabs Samples en android-architecture
  3. Android Devs Buenos Aires • Mantiene el estado de la

    vista • Bajo acoplamiento con la vista • Sobrevive a cambios de configuración (ej: Rotación de pantalla) ViewModel
  4. Android Devs Buenos Aires ¿Cómo definimos el scope de un

    ViewModel? import androidx.lifecycle.ViewModel class MyViewModel : ViewModel() { } // MyActivity.kt / MyFragment.kt val viewModel = ViewModelProviders.of(this)[MyViewModel::class.java]
  5. Android Devs Buenos Aires /** * Creates a {@link ViewModelProvider},

    which retains ViewModels while a scope of given Activity * is alive. More detailed explanation is in {@link ViewModel}. * <p> * It uses the given {@link Factory} to instantiate new ViewModels. * * @param activity an activity, in whose scope ViewModels should be retained * @param factory a {@code Factory} to instantiate new ViewModels * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(activity.getViewModelStore(), factory); }
  6. Android Devs Buenos Aires /** * Creates a {@link ViewModelProvider},

    which retains ViewModels while a scope of given * {@code fragment} is alive. More detailed explanation is in {@link ViewModel}. * <p> * It uses the given {@link Factory} to instantiate new ViewModels. * * @param fragment a fragment, in whose scope ViewModels should be retained * @param factory a {@code Factory} to instantiate new ViewModels * @return a ViewModelProvider instance */ @NonNull @MainThread public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) { Application application = checkApplication(checkActivity(fragment)); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(fragment.getViewModelStore(), factory); }
  7. Android Devs Buenos Aires ViewModelProvider /** * An utility class

    that provides {@code ViewModels} for a scope. * <p> * Default {@code ViewModelProvider} for an {@code Activity} or a {@code Fragment} can be obtained * from {@link androidx.lifecycle.ViewModelProviders} class. */ @SuppressWarnings("WeakerAccess") public class ViewModelProvider { }
  8. Android Devs Buenos Aires } public class ViewModelProvider { public

    ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) { mFactory = factory; mViewModelStore = store; } …
  9. Android Devs Buenos Aires ViewModelStore public class ViewModelStore { private

    final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel != null) { oldViewModel.onCleared(); } } public final void clear() { for (ViewModel vm : mMap.values()) { vm.clear(); } mMap.clear(); } … }
  10. Android Devs Buenos Aires Ahora bien… ¿Cómo se persisten los

    ViewModels? En el caso de Activity: static final class NonConfigurationInstances { Object custom; ViewModelStore viewModelStore; } @Override @Nullable public final Object onRetainNonConfigurationInstance() { ……. NonConfigurationInstances nci = new NonConfigurationInstances(); nci.custom = custom; nci.viewModelStore = viewModelStore; return nci; }
  11. Android Devs Buenos Aires Ahora bien… ¿Cómo se persisten los

    ViewModels? En el caso de Activity: public ComponentActivity() { … getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } }); … }
  12. Android Devs Buenos Aires Ahora bien… ¿Cómo se persisten los

    ViewModels? En el caso de Fragment: final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 { private FragmentManagerViewModel mNonConfig; } /** * FragmentManagerViewModel is the always up to date view of the Fragment’s * non configuration state */ class FragmentManagerViewModel extends ViewModel { … private final HashSet<Fragment> mRetainedFragments = new HashSet<>(); private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>(); private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>(); }
  13. Android Devs Buenos Aires Activity/ Fragment ViewModelProviders.of(this) ViewModelStore + get(ViewModelClass::class)

    - put(ViewModel) ViewModelProvider ViewModel#1 ViewModel#2 … Configuration Change
  14. Android Devs Buenos Aires class Data class MyDataViewModel : ViewModel()

    { val selected = MutableLiveData<Data>() fun select(data: Data) { selected.value = data } }
  15. Android Devs Buenos Aires class MyDataListFragment : Fragment() { private

    lateinit var viewModel: MyDataViewModel private lateinit var selector: View override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProviders.of(requireActivity())[MyDataViewModel::class.java] selector.setOnClickListener { data -> viewModel.select(data) } } }
  16. Android Devs Buenos Aires class MyDataDetailsFragment : Fragment() { private

    lateinit var viewModel: MyDataViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProviders.of(requireActivity())[MyDataViewModel::class.java] viewModel.selected.observe(viewLifecycleOwner, Observer { // Update the UI with the Data details }) } }
  17. Android Devs Buenos Aires val viewModel = MyViewModel(MyActivity()) class MyViewModel(private

    val view: MyView) interface MyView class MyActivity : FragmentActivity(), MyView
  18. Android Devs Buenos Aires class MyViewModel(private val getMyData: GetMyData) class

    GetMyData(private val repository: MyDataRepository) interface MyDataRepository class RetrofitMyDataRepository(context: Context) : MyDataRepository class MyActivity : FragmentActivity(), MyView { val viewModel = MyViewModel(GetMyData(RetrofitMyDataRepository(this))) }
  19. Android Devs Buenos Aires Observe LiveData @Test fun `observe initialised

    value`() { } //given , when val stateObserver: Observer<ViewState> = mock() val viewModel = MyViewModel() viewModel.states.observeForever(stateObserver) //then verify(stateObserver).onChanged(ViewState.Initialised)
  20. Android Devs Buenos Aires Observe LiveData @Test fun `observe livedata

    and assert values`() { } //given val stateObserver: Observer<ViewState> = mock() val viewModel = MyViewModel() viewModel.states.observeForever(stateObserver) //when viewModel.onButtonClick() //then verify(stateObserver).onChanged(ViewState.ValuableData)
  21. Android Devs Buenos Aires Assert LiveData value @Test fun `assert

    livedata value`() { } //given val viewModel = MyViewModel() //when viewModel.onButtonClick() //then assertThat(viewModel.states.value).isEqualTo(ValuableData)
  22. ViewModels
 Detrás de escena Android Devs Buenos Aires Jorge Nogueiras

    @nogueirasj @nogueiras Fabio Negri @Fabio_Negri @fabionegri