Do I need to dispose() Publishers created using LiveDataReactiveStreams - android

Let's say I have an Flowable that is shared among different parts of the application.
In each fragment where I want to observe it, I convert it to a LiveData with LiveDataReactiveStreams.fromPublisher to avoid leaks and crashes. I now have a LiveData that wraps my Flowable.
I then pass the LiveData to my ViewModel (in the ViewModelFactory). As far as I understand, I can go ahead and use the LiveData without worrying about leaks.
Now, instead of observing the LiveData directly, I am tempted to convert it back to a Flowable with LiveDataReactiveStreams.toPublisher and Flowable.fromPublisher and subscribe to the Flowable instead. This is now a Flowable that wraps a LiveData which wraps a Flowable
My question is: Do I have to worry about disposing the subscriptions to this Flowable? My hope is that the LiveData will act as a "barrier", preventing my context to leak back up to the root Flowable, but I am not so sure about that.
In other words:
Flowable A exists in a global context
In each fragment, A is wrapped in LiveData B which is set as a property of the fragments ViewModel
When normally I would observe LiveData B, I instead wrap it in Flowable C
I subscribe to Flowable C and ignore the returned disposable
Will views accessed in C leak up to A when the fragment is destroyed?

Considering the current implementation, you still need to care for the subscriptions manually. The lifecycle is only used for handling the observation of the live data.
mLiveData.observe(mLifecycle, LiveDataSubscription.this);
The observation is only canceled automatically when a non-positive amount of items was requested and an error is sent. This then disposes the subscription. Since the producer never completes it'll never dispose the subscription by itself and thus you'll leak the subscription if you don't dispose it yourself.

Related

Is it possible to observe data in domain usecase/interactor?

I want to know if it is possible to somehow observe the data in the domain layer's usecases classes, that are comming from repository. I would wanted to observe live data there, like in activity/fragement:
pictures.observe(...) { pictures -> }
Is it possible with LiveData or Flow?
I would say that it is against the Clean Architecture principle. LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. So it should be used in the UI layer.
LiveData.observe() method accepts LifecycleOwner as an argument, so you need somehow to pass the instance of LifecycleOwner to UseCase/Interactor.
There is also LiveData.observeForever() method, which doesn't accept LifecycleOwner object, but LiveData.removeObserver() should be called to stop observing the LiveData.

Will data binding unregister listeners from a ViewModel implementing Observable?

I have some more complex logic for data provided by my ViewModel to the UI, so simply exposing the data via LiveData won't do the job for me. Now I've seen in the Android docs that I can implement Observable on my ViewModel to get the fine-grained control I need.
However in the documentation it also says:
There are situations where you might prefer to use a ViewModel
component that implements the Observable interface over using LiveData
objects, even if you lose the lifecycle management capabilities of
LiveData.
How intelligent is the built-in Android data binding? Will it automatically unregister it's listeners when necessary (e.g. on configuration changes where the View is destroyey) so that I don't have to care about the lost lifecycle capabilities? Or do I have to watch the Lifecycle of the View and unregister it's listeners? (=do manually what LiveData normally does for me).
How intelligent is the built-in Android data binding? Will it automatically unregister it's listeners when necessary (e.g. on configuration changes where the View is destroyey) so that I don't have to care about the lost lifecycle capabilities? Or do I have to watch the Lifecycle of the View and unregister it's listeners? (=do manually what LiveData normally does for me).
So I did some tests. I implemented androidx.databinding.Observable on my ViewModel and did a configuration change with the following log calls:
override fun removeOnPropertyChangedCallback(
callback: androidx.databinding.Observable.OnPropertyChangedCallback?) {
Log.d("APP:EVENTS", "removeOnPropertyChangedCallback " + callback.toString())
}
override fun addOnPropertyChangedCallback(
callback: androidx.databinding.Observable.OnPropertyChangedCallback?) {
Log.d("APP:EVENTS", "addOnPropertyChangedCallback " + callback.toString())
}
I saw that addOnPropertyChangedCallback was invoked for each time my viewmodel was referenced in a layout binding expression. And not once did I see removeOnPropertyChangedCallback invoked. My initial conclusion is that AndroidX databinding is dumb and does not automagically remove the listener.
FYI: the callback type was ViewDataBinding.WeakPropertyListener
However, I took a peek at ViewDataBinding.java source code and found that it is using Weak References to add the listener.
So what this implies, is that upon a configuration change, Android OS should be able to garbage collect your Activity/Fragment because the viewmodel does not have a strong reference.
My advice: Don't add the boilerplate to unregister the listeners. Android will not leak references to your activities and fragments on configuration changes.
Now, if you choose not to use LiveData, consider making your viewmodel implement LifecycleObserver so that you can re-emit the most recent value when your Activity/Fragment goes into the active state. This is the key behavior you lose by not using LiveData. Otherwise, you can emit notifications by using the PropertyChangeRegistry.notifyCallbacks() as mentioned in the documentation you shared at some other time. Unfortunately, I think this can only be used to notify for all properties.
Another thing... while I've not verified the behavior the source code seems to indicate that weak references are used for ObservableField, ObservableList, ObservableMap, etc.
LiveData is different for a couple of reasons:
The documentation for LiveData.observe says that a strong reference is held to both the observer AND the lifecycle owner until the lifecycle owner is destroyed.
LiveData emits differently than ObservableField. LiveData will emit whenever setValue or postValue are called without regard to if the value actually changes. This is not true for ObservableField. For this reason, LiveData can be used to send a somewhat "pseudo-event" by setting the same value more than once. An example of where this can be useful can be found on the Conditional Navigation page where multiple login failures would trigger multiple snackbars.
Nope. ViewModel will not unregister Observable subscription automatically. You can do it manually though. It is pretty easy.
Firstly you create CompositeDisposable
protected var disposables = CompositeDisposable()
Secondly, create your Observable(it may be some request or UI event listener) subscribe to it and assign its result to CompositeDisposable
disposables.add(
someObservable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ data ->
// update UI or some ObservableFields for view/databinding
}, { exception ->
// handle errors here
})
)
The last thing you should do is to override ViewModel's method onCleared() like this:
override fun onCleared() {
super.onCleared()
disposables.clear()
}
This way all subscription added to your CompositeDisposable will be cleared automatically
Edit
I showed only the example. You may add triggers in onConfigurationChanged or onCreate or onResume to clear subscriptions as well - but it is dependent on specific usecases of an app. I gave just a general one.
Hope it helps.
DataBinding would not do the unregistering for you. Its simply help bind your layout file and the ViewModel. It is the viewModel that will protect you from device's configuration change. You still need to apply onSavedViewState() in your base activity or fragment as viewModel does not cover that. As per unregistering, LiveData does that.
As #Pavio already taught you how to create Observable, that is RxJava working. I would suggest using kotlin's coroutines and viewModel with LiveData to get the best out of your situation. Rx has a learning curve to it, although it does offer hundred of operators for all sorts of operations. If you really want to learn the kotlin way, look into kotlin flows and channels.
If i was in your place, I would solve my problem with ViewModels, LiveData and Coroutines.

What are the benefits of returning a live data of a retrofit response

In a googlesample I have seen that the Retrofit call object is returned as a LiveData instance.
#GET("users/{login}")
fun getUser(#Path("login") login: String): LiveData<ApiResponse<User>>
like so.
What benefits does this have over just waiting for the CallBack to be invoked.
LiveData is one of architecture components and it's lifecycle-aware meaning that it ensures to update ui only when your lifecycle is in active state.There can be situation where you get data from network but your activity or fragment is destroyed and you update your view which may resulted in leak or crash. You can solve this problem with the help of livedata. It's worth mentioned that you can also use RxAndroid observable but you won't get lifecycle-awareness functionality.

How to make the viewmodel observe the repository without lifecycle object

I'm building an android application with MVVM design and I have multiple layers(view, ViewModel, repository and dataSource both local and remote).
I want that my repository object will observe the dataSources, do all of his logic for how and when storing cache, map the data to the correct form for the above layer and only then notify the view model about the new data.
Similar to that I want the ViewModel to observe the repository for new data arrival, then doing all the business logic and only after that notify the view.
My problem is that LiveData require lifecycle object that the ViewModel and
the repository doesn't have.
I read about using simple observable rather than LiveData but I also read that it is bad practice because the observables are alive forever and this could lead to wierd crashes. In addition, I have PageKeyedDataSource which only returns LiveData.
I also read about using Transformations.map but what if I don't want only to map the data but rather to do something more complex.
Is There a way to make one layer safely observe another layer without creating a chain of LiveData observables from the view layer to the DataSource?
note: my ViewModel is used in multiple fragments if that somehow relevant.
My problem is that LiveData require lifecycle object
Actually, lifecycle is optional. There is an observeForever(Observer) method in LiveData that does not require lifecycle. But that means you should also manually call removeObserver(Observer) when your repository finished work, otherwise it will be a leak.
This really is not much different to using Rx's observables. In both cases you should override onCleared() in your viewmodel and manually unsubscribe (or remove observer) from the repository.
observables are alive forever and this could lead to wierd crashes
Nope, they are alive until you dispose them, but you must do it manually as Rx does not provide lifecycle-aware subscription.
Is There a way to make one layer safely observe another layer without creating a chain of LiveData observables from the view layer to the DataSource?
As your wrote, you are trying to do a View-that-observes-ViewModel-that-observes-Repo-that-observes-DataSource. It is already a chain, you should deal with it.
It is possible to to safely observe LiveData from ViewModel, cause View has a lifecycle and LiveData has lifecycle-aware observe.
But Repo and DataSource do not have lifecycles thus you should manually manage subscriptions. It is possible both with LiveData and Rx observables - you can choose whichever you prefer.
1.First of all, if you need context in viewmodel use AndroidViewModel .
2.Don't do any business logic in Viewmodel class as it's just mediator rather do calculations in repository class
3.use rxjava/rxkotlin in repository and return observable object to Viewmodel from repository method, once Viewmodel gets notified update view.
You could use DataBinding to reflect the ViewModel data in you UI. https://developer.android.com/topic/libraries/data-binding
An added benefit is that you write less boilerplate code.

RxJava with Presenter and retained fragment for configuration changes

I'm new to RxJava and using this together with MVP architecture.
I've found a few examples on saving observables upon configuration changes using a retained fragment(still not sure if this is the best way to do it). The examples I've found though is handling observables directly on the Activity or Fragment, not from a Presenter.
So I experimented and set up this quick example(using only Reactivex's RxJava and RxAndroid lib) just to test, which seems to work fine. What this example does is:
Initiates an activity with a headless retained fragment.
Push button
Presenter calls a FakeService for a delayed(5seconds) response observable.
Presenter does .cache() on this observable.
Presenter tells the view to retain this observable.
View saves the observable in a retained fragment.
Presenter subscribes to observable.
User does a configuration change(device rotation). User can do this as many times as he wants.
OnPause tells the Presenter's CompositeSubscription to clear and unsubscribes from all current subscriptions.
Activity gets recreated and reuses the existing retained fragment.
Activity's onResume checks if the retained fragment's stored observable is null.
If not null, tells the Presenter to subscribe to it.
The retained observable gets subscribed to, and because .cache was called, it just replays the result to the new subscriber without calling the service again.
When the Presenter shows the final result to the view, it also sets the retained fragment's saved observable to null.
I'm wondering if I'm doing this properly, and if there's a more efficient or elegant way to handle configuration change when the observable's subscription is being handled in a Presenter?
Edit:
Thanks for the feedback.
Based on this I've reached what I think is a cleaner solution, and I've updated my linked example with the changes.
With the new change; instead of passing the Observable from the Presenter to the Activity to the retainedFragment to be stored incase of a configurationChange event, I rather set the retainedFragment as a second "view" to the Presenter when it's created.
This way when onResume() happens after device rotation, I don't need to make the Activity do the ugly plumbing of passing the Observable from the retainedFragment back to the Presenter.
The Presenter can just interact with this second "view" directly and check for the retained observable itself and resubscribe if needed. The main Activity no longer needs to know about this observable. Suddenly it's a much simpler view layer.
Looks good, you can see that example - https://github.com/krpiotrek/RetainFragmentSample
Sounds about right, good job! Some suggestions:
You could just use Activity.onRetainNonConfigurationInstance(). I've heard it's getting un-deprecated in Android N. You can continue to use retained fragment if you like it, there's no problem with that, but you don't have to if you preferred not to use fragments.
Why only retain the observable and not the whole presenter? It seems maybe a bit wasteful to create a new presenter, maybe you can make it work with same instance that can "attach" and "detach" a view. But then again you have to deal with what to do if your observable emits while you are detached from any views, so maybe that's good enough.
Dan Lew recently made a case in his Droidcond SF talk that you shouldn't use cache(). He says replay() gives you greater control over what's happening and replay().autoconnect() works the same as cache(). He convinced me, but see for yourself.
This library https://github.com/MaksTuev/ferro contains another way for store screens data and managing background tasks.
you scenario will looks like this
Open Activity, create presenter
Push Btn
Presenter calls a FakeService for a delayed(5seconds) response observable.
Configuration changed, presenter isn't destroyed, Observable isn't unsubscrubed, all rx event is frozen
Activity recreated, presenter reused, presenter show on view previously loaded data, all rx event is unfrozen
I think this help

Categories

Resources