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
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,310