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

Android Architecture Re-evolution

Sebas LG
November 18, 2017

Android Architecture Re-evolution

Walk through Android architecture and design patterns with a deep dive in the new ViewModel and LiveData Android Architecture Components

Sebas LG

November 18, 2017
Tweet

Other Decks in Programming

Transcript

  1. ViewModel Activity lifecycle is complex, Fragment lifecycle is a death

    trap. ViewModel is a class that contains view data and logic separated from lifecycle-bound entities like Activities and Fragments Why & What
  2. class MyActivity : AppCompatActivity() { public override fun onCreate() {

    val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) } } • No more reference to Views. Ever • A factory can be used for more complex ViewModels • Created once and shared with multiple Fragments ViewModel How class MyActivity : AppCompatActivity() { public override fun onCreate() { val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) } } class MyViewModel : ViewModel() { } class MyActivity : AppCompatActivity() { public override fun onCreate() { val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) titleTextView.text = viewModel.title } } class MyViewModel : ViewModel() { val title = "Great title" } class MyActivity : AppCompatActivity() { public override fun onCreate() { val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) titleTextView.text = viewModel.title redButton.onClick { viewModel.redButtonClicked() } } } class MyViewModel : ViewModel() { val title = "Great title" fun redButtonClicked() { ... } }
  3. • Survive rotation • Easy to use No persistence when

    app killed Store data with existing solutions God object candidate Follow same practices as for presenters ViewModel Pros & Cons
  4. • Observable pattern like RxJava but Lifecycle-Aware • No more

    onPause, onStart, etc. LiveData We can’t push data from a ViewModel into an Activity or Fragment We need a data provider in our ViewModel so the View can subscribe to it Why & What LiveData is a lifecycle-aware observable that holds data and provides updates
  5. onCreate() => LiveData is observed and value is updated but

    not emitted to any Observer onStart() => LiveData value is emitted to all Observers on main thread onStop() => LiveData value is updated but not emitted to any Observer onDestroy() => LiveData stops being observed LiveData How
  6. class MyViewModel : ViewModel() { val userName = MutableLiveData<String>() }

    LiveData How class MyActivity : AppCompatActivity() { public override fun onCreate() { val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) } } class MyViewModel : ViewModel() { val userName = MutableLiveData<String>() fun updateUserName(newUserName: String) { userName.value = newUserName // In main thread or userName.postValue(newUserName) // In background thread } } class MyActivity : AppCompatActivity() { public override fun onCreate() { val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java) viewModel.userName .observe(this, Observer { value -> updateUserTextView(value) }) } }
  7. LiveData How class MyViewModel : ViewModel() { private val userLiveData

    = MutableLiveData<UserInfo>() val userName: LiveData<String> = Transformations.map(userLiveData, user -> { user.name + " " + user.lastName }) } Basic transformations with map and switchMap
  8. LiveData & RxJava • Interoperability between LiveData and RxJava with

    the extensions: LiveDataReactiveStreams.fromPublisher() • Override ViewModel.onCleared() to clear any ongoing RxJava subscription
  9. • Forget to use postValue() from a background thread •

    Fragments start observing in onCreateView() but you need to manually remove Observer in onDestroyView() Pitfalls
  10. • User wants to see the location of a painting

    in the map • User wants to see her location on the map when moving • User wants to see a route on the map from her location to painting Let’s practice
  11. MapViewModel class MapViewModel : ViewModel() { private val userLocation =

    MutableLiveData<Location>() private val paintingLocation = MutableLiveData<Location>() class MapViewModel : ViewModel() { private val userLocation = MutableLiveData<Location>() private val paintingLocation = MutableLiveData<Location>() fun userSelectedPainting(location: Location) { // Updated from UI paintingLocation.value = location } class MapViewModel : ViewModel() { private val userLocation = MutableLiveData<Location>() private val paintingLocation = MutableLiveData<Location>() fun userSelectedPainting(location: Location) { // Updated from UI paintingLocation.value = location } private val locations = MediatorLiveData<Pair<Location, Location>>().apply { class MapViewModel : ViewModel() { private val userLocation = MutableLiveData<Location>() private val paintingLocation = MutableLiveData<Location>() fun userSelectedPainting(location: Location) { // Updated from UI paintingLocation.value = location } private val locations = MediatorLiveData<Pair<Location, Location>>().apply { addSource(userLocation) { newLoc -> value = Pair(newLoc, value.second) } addSource(paintingLocation) { newLoc -> value = Pair(value.first, newLoc) } }
  12. MapViewModel private val liveRoute: LiveData<Route> = Transformations .switchMap(locations, { (userLoc,

    paintingLoc) -> planRoute(userLoc, paintingLoc) }) private val liveRoute: LiveData<Route> = Transformations .switchMap(locations, { (userLoc, paintingLoc) -> planRoute(userLoc, paintingLoc) }) val mapViewState = MediatorLiveData<MapViewState>().apply { addSource(locations) { newLoc -> value = MapViewState(newLoc.first, newLoc.second, value.route) } addSource(liveRoute) { newRoute -> value = MapViewState(value.userLoc, value.paintingLoc, newRoute) } } data class MapViewState( val userLoc: Location?, val paintingLoc: Location?, val route: Route? )
  13. MapActivity class MapActivity : AppCompatActivity() { public override fun onCreate()

    { val viewModel = ViewModelProviders.of(this).get(MapViewModel::class.java) viewModel.mapViewState .observe(this, Observer { newMapViewState -> updateMapView(newMapViewState) }) } }