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
Related
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.
I've got a presenter that is created for the RecyclerView.Adapter. The presenter has a method to call into the API layer, then change some local data values. This is implemented as an Rx chain:
public void doStuff(Object args) {
mRemote.doStuff(args)
.doOnNext(count -> mLocal.setStuffCount(count))
.firstOrError()
.subscribeOn(mSchedulerProvider.io())
.observeOn(mSchedulerProvider.ui())
.subscribe(...)
<...>
This method might be called several times with different arguments, and therefore generate different subscriptions. As I understand, RxJava will unsubscribe onComplete or onError. However, it is possible that there are, for example, 5 subscriptions created, and before they are resolved, the user moves on from the fragment, and therefore the Adapter is no longer relevant and should be collected. However, as I understand, it won't be collected cause of the active subscriptions, until those are resolved.
Normally I link the presenter with the related view, then store all subscriptions in a CompositeDisposable and clear the disposable when the lifecycle suggests it's time to clear (for example, a view goes to the onPause state). I could implement a similar approach here, however that means I would have to:
1) Establish a relationship between a view and the adapter, so that when the view's lifecycle method is triggered, it will take the adapter and invoke an unsubscribe method on it, which will then take the presenter and invoke clear(). Could I rely on onDetachedFromRecyclerView for that? Or would I have to do that manually?
2) Each time my presenter method to do stuff is called, I'll be creating a new subscription, which means the CompositeDisposable will grow in size until who know's what. Although the call has been executed, the disposable is still referenced in the CompositeDisposable.
I have a feeling there's a simpler solution here so hoping someone might point me at it.
EDIT:
Another solution that comes to mind, would be to create a presenter for each ViewHolder and link subscription to the binding and recycling of the holder. However, that means keeping a few more presenters hanging around - but the approach seems a bit cleaner.
Not sure if this is going to be useful to anyone but this is how I've solved this problem.
I ended up merging my Presenters while keeping different View interfaces for the ViewHolder and the Fragment (will call them ViewHolderView and FragmentView).
I liked the idea of having a Presenter for each ViewHolderView, however, it didn't really solve my problem. I was keeping my CompositeDisposable inside the Presenter, but with that approach, I had to move it out to the Adapter level and keep a reference to the Disposable inside my ViewHolder. Similar to the answer suggested here
I didn't like the idea of manipulating the Adapter from the Fragment lifecycle manually as I was already calling subscribe() and unsubscribe() on FragmentView, and onDetachedFromRecyclerView is not called unless you nullify the adapter on the RecyclerView
The solution was to merge my Adapter presenter with my Fragment presenter while keeping the ViewHolderView interface. So I ended up with a contract for one Presenter, and two separate View interfaces, and I'm passing the Presenter to the Adapter. When FragmentView unsubscribes, it cleans up everything.
Currently I'm working on project which is using RxJava together with RxBinding to observe views' changes. It's working really well for fragments and activities where we have easy access to life-cycle events - as it's recommended we bind to data streams on onResume and unbind on onPause.
Lately we've introduces RecyclerView, which display list of views and all of them can be data stream which we would like to subscribe to. The problem which I faced is passing CompositeSubscription object from activity/fragment through adapter down to view holders when they are created. Of course it doesn't work well ViewHolders won't be recreated when user leaves a screen and comes back (onPause and onResume are being called).
The solution would be to make adapter, layout manager (to access existing view holders) life cycle aware, but it require from us to write extra code to pass those subscriptions reference between logic layers.
However one of my colleagues proposed to use event bus, which would be used to pass Subscription in a event to activity/fragment, where they'll be added to CompositeSubscription and all of them will be unsubscribed all together. Moreover we can inform view holder to subsribe themself when user returns.
What do you think about this approach? Are there any pitfalls which I should be aware of in this approach?
Do not make your Views lifecycle aware. This violates separation of concerns.
Just use clickListeners upon viewBind in the Adapter.
Don't pass the Subscription to the adapter. (The adapter doesn't need to know about it, nor control it's lifecycle) The adapter could just offer an Rx-endpoint that you subscribe to in the (for example) Activity onStart and unsubscribe in onStop. Then Adapter can handle the marshalling of click events on items into an Observable.
Remember: You shouldn't apply RxJava to every problem.
Firstly, I know that with Model View Presenter there are different implementations, and in my mind as long as you have the layers of abstraction clearly defined and doing their appointed roles then how you implement this pattern is open to interpretation. I have been implementing this pattern in quite a few apps where there was just one Activity. I've now started a new project that has multiple Activities and attached Fragments, including nested fragments (ViewPager).
I'm now trying to translate the MVP to this project and I've hit a concept wall and would like some guidance and insights.
So far I've created the above structure and started to do a 1 : 1 relationship with View & Presenter (regardless of Activity or Fragment). I feel that this is OK, however if for example I sent a request to do something from an Activity View to its Presenter, which returns a result to the Activity View how would I go about propagating the result i.e. update all the other Activities/Fragments that are currently not in a Paused() or Stop() state. I feel like in this case there should be a central Presenter that updates all necessary Activity and Fragment Views, but I'm not sure how to go about doing this.
Currently when each Activity and Fragment is created it creates a new instance of a Presenter class, passing in itself as a reference (the Activities and Fragments implement their own interfaces), which the presenter stores as a WeakReference and can invoke the relevant interface methods when returning a result.
According to the docs whenever Fragments want to communicate with one another and the attached Activity you should use a callback interface. With this in mind should I have one callback interface that the Activity implements and the Fragments callback to whenever they request something, so in essence only the Activity would have a Presenter and Model layer that the Fragments have to callback to in order to make various requests?
Sorry if this sounds a bit muddled, hopefully this is clear enough to understand what I want to achieve, and if I’m thinking along the right lines... or totally off the mark!
I think this is okay to have a presenter inside activity. Basically activity is like a controller, it should know about the presenter.
It would be wrong to put a presenter inside a fragment if activity or other fragment needs it too. You can put a presenter inside a fragment only if this presenter is designed specifically for fragment.
which the presenter stores as a WeakReference and can invoke the relevant interface methods when returning a result
Why do you need a WeakReference here? If you have 1:1 relationship then I assume your presenter does not have it's own lifecycle, meaning that it's lifecycle depends on either activity or fragment. There is no risk of having memory leaks because it's not a singleton, right? It should be safe to have a strong reference.
I'm not sure if I answered your question because it looks a bit broad to me. My point is that, fragments are just separated "parts" of activity and you should treat them as parts. If presenter belongs to this part only, then it should be inside. Otherwise it should be in activity. You are right about using an interface to access activity, this is a well-known design approach which Google uses in their examples.
Nope, no interface anymore. You either use RxJava Observables to update all the views as described here or some kind of Bus(Otto-deprecated or EventBus). And you will like it because they make interacting too easy.
I'm still figuring out RxJava and using it to do some networking stuff with Retrofit 2. Been trying it our for a a couple days and like that the code looks more readable now but have come across an issue that I cant seem to figure a way around.
I am trying to perform a login (which returns an API token) and then use this token to fetch some initial data all in the same chain so that the output of the chain is the token + data. To do this I call my API service with a
apiClient
.login()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(token -> getData(token))
.subscribe(new Subscrber<Bundle>() {...});
This seemed fine, but I also wanted to show a progress bar when starting and stopping the chain. So I added a .doOnSubscribe() and a .doOnUnsubscribe() to this as well. However I noticed that after orientation change the fragment that I was trying to hide the progress bar is always null.
So I searched and came across the RxLifecycle lib that seemed like it would help and I now .cache() and unsubscribe from the event chain. But I cant figure out how to subscribe to the same event again in onCreate() after this? I think I'm missing something pretty basic and would appreciate any help with this.
You don't necessarily have to use any architecture pattern to accomplish that. Though any MVP/MVC are nice things for concerns separation, testing etc, making your Controller/Presenter/DAO an application-wide singleton, which keeps up memory through whole application's life is not exactly a good idea.
Here's a sample project using retained fragment instance and RxJava - https://github.com/krpiotrek/RetainFragmentSample
The main idea there is to use Fragment with setRetainInstance(true) called, which protects it from being destroyed on orientation change, and store your Observable in there. Here's how you handle that in Activity/Fragment onCreate
protected void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
// first run, create observable
mInfoObservable = createInfoObservable();
// set Observable in retained fragment
RetainFragmentHelper.setObject(this, getSupportFragmentManager(), mInfoObservable);
} else {
// following runs, get observable from retained fragment
mInfoObservable = RetainFragmentHelper.getObjectOrNull(this, getSupportFragmentManager());
}
// subscribe
mInfoObservable.subscribe(...);
}
Keep in mind that your Observable has to cache last value, one way is to use cache() operator.
You need to make sure that you subscribe to the same Observable instance that is returned from .cache(). Typically you would store this instance somewhere in a Singleton (like the Application class), a retained fragment or an Android Service.