Viraj Tank

Sociomantic Labs

@viraj49

Viraj Tank

Sociomantic Labs

@viraj49

God Activity Problem

Rise of Architectures

God P/VM Problem

Clean Architecture

Lifecycle Question

Bind/Subscribe - onResume/onStart

UnBind/Dispose - onPause/onStop

View

Partially/

Fully

Blocked

View

Partially/

Fully

Blocked

View Reloading Problem

RunTime Config Change

e.g. Orientation, Keyboard availability, Language, Docking etc

Android restarts running Activity

  • Data call may get interrupted

  • Context Leak

  • View state management

- Lifecycle changes

- Configuration changes

  • Reloading the view

  • Data call may get interrupted

  • Context Leak

  • View state management

Problem Summary

MVP Activity/Fragment

Bind - onStart

Dispose - onDestroy*

UnBind - onStop

Activity

!isConfigurationChanging

Bind - onStart

Dispose - onDestroy

UnBind - onStop

Fragment

RxJava Subject

  • Hot Observable

  • Behavior / Publish

  • RxRelay

Activity


  class MyActiviy : Activity() {

    private val myPresenterFactory = MyPresenterFactory()
    private lateinit var myPresenter: MyPresenter

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      myPresenter = myPresenterFactory.getPresenter()
    }

  class MyActiviy : Activity() {

    private val myPresenterFactory = MyPresenterFactory()
    private lateinit var myPresenter: MyPresenter

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      myPresenter = myPresenterFactory.getPresenter()
    }

    override fun onStart() {
      super.onStart()
      myPresenter.bind(this)
    }

    override fun onStop() {
      myPresenter.unBind()
      super.onStop()
    }

  class MyActiviy : Activity() {

    private val myPresenterFactory = MyPresenterFactory()
    private lateinit var myPresenter: MyPresenter

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      myPresenter = myPresenterFactory.getPresenter()
    }

    override fun onStart() {
      super.onStart()
      myPresenter.bind(this)
    }

    override fun onStop() {
      myPresenter.unBind()
      super.onStop()
    }

    override fun onDestroy() {
      if (!isChangingConfigurations) {
        myPresenter.dispose()
      }
      super.onDestroy()
    }
  }

Fragment


  class MyFragment : Fragment() {

    private val myPresenter = MyPresenter()

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      retainInstance = true
    }

  class MyFragment : Fragment() {

    private val myPresenter = MyPresenter()

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      retainInstance = true
    }

    override fun onStart() {
      super.onStart()
      myPresenter.bind(this)
    }

    override fun onStop() {
      myPresenter.unBind()
      super.onStop()
    }

    override fun onDestroy() {
      myPresenter.dispose()
      super.onDestroy()
    }
  }

  class MyFragment : Fragment() {

    private val myPresenter = MyPresenter()

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      retainInstance = true
    }

    override fun onStart() {
      super.onStart()
      myPresenter.bind(this)
    }

    override fun onStop() {
      myPresenter.unBind()
      super.onStop()
    }

Presenter


  class MyPresenter {
    private val hotObservable = BehaviorSubject.create<Model>()

    private val viewSubscriptions = CompositeDisposable()
    private val dataSubscriptions = CompositeDisposable()

  class MyPresenter {
    private val hotObservable = BehaviorSubject.create<Model>()

    private val viewSubscriptions = CompositeDisposable()
    private val dataSubscriptions = CompositeDisposable()

    fun bind(view: MyFragment) {
      viewSubscriptions.add(hotObservable
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe({
                this.view.updateView(it)
              }))
    }

  class MyPresenter {
    private val hotObservable = BehaviorSubject.create<Model>()

    private val viewSubscriptions = CompositeDisposable()
    private val dataSubscriptions = CompositeDisposable()

    fun bind(view: MyFragment) {
      viewSubscriptions.add(hotObservable
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe({
                this.view.updateView(it)
              }))
    }

    fun unBind() {
      viewSubscriptions.clear()
      this.view = null
    }

Presenter


  class MyPresenter {

    
    
    

    fun getData() {
      dataSubscriptions.add(myDataSource.getData())
              .subscribeOn(Schedulers.computation())
              .observeOn(Schedulers.computation())
              .subscribe({ modelData ->
                hotObservable.onNext(modelData)
              })
    }

  class MyPresenter {

    init {
      getData()
    }

    fun getData() {
      dataSubscriptions.add(myDataSource.getData())
              .subscribeOn(Schedulers.computation())
              .observeOn(Schedulers.computation())
              .subscribe({ modelData ->
                hotObservable.onNext(modelData)
              })
    }

  class MyPresenter {

    init {
      getData()
    }

    fun getData() {
      dataSubscriptions.add(myDataSource.getData())
              .subscribeOn(Schedulers.computation())
              .observeOn(Schedulers.computation())
              .subscribe({ modelData ->
                hotObservable.onNext(modelData)
              })
    }

    fun dispose() {
      dataSubscriptions.dispose()
    }
  }
  • Reloading the view

  • Data call may get interrupted

  • Context Leak

  • View state management

View State Management

  • View data

  • State changing action

  • One time action

View Data

State Changing Action

Unidirectional Dataflow


  data class MyViewState(val myData: String = "", 
                         val loadingState: Boolean = false)

  data class MyViewState(val myData: String = "", 
                         val loadingState: Boolean = false)

  class MyPresenter {
    private var myViewState = MyViewState()
    private val hotObservable = BehaviorSubject.create<MyViewState>()

  data class MyViewState(val myData: String = "", 
                         val loadingState: Boolean = false)

  class MyPresenter {
    private var myViewState = MyViewState()
    private val hotObservable = BehaviorSubject.create<MyViewState>()

    fun getData() {
      myViewState = myViewState.copy(myData = data, loadingState = false)
      hotObservable.onNext(myViewState)
    }
  }

One Time Action

isConsumed


  data class MyViewState(val isConsumed: Boolean = true,
                         val errorMessage: String = "")

  data class MyViewState(val isConsumed: Boolean = true,
                         val errorMessage: String = "")

  class MyPresenter {
    private var myViewState = MyViewState()
    private val hotObservable = BehaviorSubject.create<MyViewState>()

  data class MyViewState(val isConsumed: Boolean = true,
                         val errorMessage: String = "")

  class MyPresenter {
    private var myViewState = MyViewState()
    private val hotObservable = BehaviorSubject.create<MyViewState>()

    fun getData() {
      myViewState = myViewState.copy(isConsumed = false, errorMessage = "Error")
      hotObservable.onNext(myViewState)
    }

  data class MyViewState(val isConsumed: Boolean = true,
                         val errorMessage: String = "")

  class MyPresenter {
    private var myViewState = MyViewState()
    private val hotObservable = BehaviorSubject.create<MyViewState>()

    fun getData() {
      myViewState = myViewState.copy(isConsumed = false, errorMessage = "Error")
      hotObservable.onNext(myViewState)
    }

  
  
  
  
  

  class MyFragment : Fragment() {
    fun updateView(myViewState: MyViewState) {
        if (!myViewState.isConsumed) {
            showError()
            myPresenter.errorConsumed()
        }
    }
  }

  data class MyViewState(val isConsumed: Boolean = true,
                         val errorMessage: String = "")

  class MyPresenter {
    private var myViewState = MyViewState()
    private val hotObservable = BehaviorSubject.create<MyViewState>()

    fun getData() {
      myViewState = myViewState.copy(isConsumed = false, errorMessage = "Error")
      hotObservable.onNext(myViewState)
    }

    fun errorConsumed() {
      myViewState = myViewState.copy(true, "")
      hotObservable.onNext(myViewState)
    }
  }

  class MyFragment : Fragment() {
    fun updateView(myViewState: MyViewState) {
        if (!myViewState.isConsumed) {
            showError()
            myPresenter.errorConsumed()
        }
    }
  }

MVP + Subjects

MVVM + Subjects

Android Architecture Components

  • Lifecycle

  • ViewModel

  • LiveData

  • Room

AAC- core concept

Should I use ViewModel with MVP?

NO

Can I use ViewModel with MVP?

YES

AAC - ViewModel

  • Retained Fragment Storage

  • Lifecycle Aware

  • onDestroy() clean up

AAC - LiveData

  • ~ Hot Observables

  • Lifecycle Aware

  • STARTED or RESUMED

  • onDestroy() clean up

Activity


  class MyActiviy : LifecycleActivity() {

    private val myViewModelFactory = MyViewModelFactory()
    private lateinit var myViewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      myViewModel = ViewModelProviders.of(this, myViewModelFactory)
                                      .get(MyViewModel::class.java)

      myViewModel.myLiveData.observe(this, Observer {
        /* Update View */
      })
    }
  }

Fragment


  class MyFragment : LifecycleFragment() {

    private val myViewModelFactory = MyViewModelFactory()
    private lateinit var myViewModel: MyViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      myViewModel = ViewModelProviders.of(this, myViewModelFactory)
                                      .get(MyViewModel::class.java)

      myViewModel.myLiveData.observe(this, Observer {
        /* Update View */
      })
    }
  }

ViewModel


  class MyViewModel : ViewModel() {
    private val dataSubscription = CompositeDisposable()

    private var myViewState = MyViewState()
    val myLiveData = MutableLiveData<MyViewState>()

    init {
      getData()
    }

    fun getData() {
      dataSubscriptions.add(myDataSource.getData())
              .subscribeOn(Schedulers.computation())
              .observeOn(Schedulers.computation())
              .subscribe({ data ->
                myViewState = myViewState.copy(myData = data)
                myLiveData.postValue(myViewState)
              })
    }

    override fun onCleared() {
      dataSubscription.dispose()
      super.onCleared()
    }
  }

Summary

- Lifecycle changes

- Configuration changes

  • Reloading the view

  • Data call may get interrupted

  • Context Leak

  • Recreating view state

MVP

- Subject based solution

- AAC based solution

MVVM

- AAC based solution

Thanks

github.com/viraj49

twitter.com/viraj49

speakerdeck.com/viraj49

Android Architecture Components

By Viraj Tank

Android Architecture Components

Internals of Android Architecture Components & making the best use of it in MVVM and MVP

  • 1,207