Use event bus to pass RxJava subscriptions - android

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.

Related

Synchronize the UI and data of the recyclerview in the current fragment with the UI and data of the recyclerview in the adjacent fragment

This is the 'like' feature on Facebook.
I would like to synchronize these recyclerviews with these two pieces.
If you click on the 'Like' button on the recyclerview in one piece, the 'Like' button on the recyclerview should change when you change to another piece.
Which method should I use?
interface?
service?
Map Should I use this?
What method do you use to synchronize the data of two fragments?
You should be using ViewModel's from architecture components.
https://developer.android.com/topic/libraries/architecture/.
Basically you create a view model in the activity so that it is stored with the activity scope
//this is the instance of the activity
ViewModelProviders.of(this)
You can then get an instance of this view model in each fragment using
ViewModelProviders.of(getActivity())
The view model can then be used like in a standard MVVM architecture.
Each fragment should register to the lifecycle aware components that the ViewModel would provide. MutableLiveData is one such component that you could use to provide the data back to whoever is interested in the data (in this case each fragment)
Be aware that LiveData while does a fantastic job can be limited as it stores data as a state in time. This is great, but android should be developed where it is driven by events)
As an example If you have a viewmodel which sends data to the view via livedata it could trigger a dialog. When the user closes that dialog and causes a configuration change (destroys and recreates the activity) the view will receive the state of the live data at the point in time it was set which will again show the dialog. Basically each time you rotate the device it could show the dialog agian even though you've dismissed it.
A hacky fix to this is to notify the viewmodel to remove the state in the livedata after the dialog is dismissed. but this creates a number of other issues including tying view state with the viewmodel
It's a lot more flexible if the Lifecycle aware component instead sends events of when data changes. Think Rxjava that is lifecycle aware. You add the data to the RXJava component and the observable provides the data to the observer when the view is in a state to consume it (> onresume and < ondestory).
Hopefully that gives you a starting point. Let me know if you need more details

RxJava unsubscribing in RecyclerView adapter presenter

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.

Android ViewModel management in mvvm

I'm creating a sample app (Last-Mvvm) to learn (and possibly show) the usage of mvvm pattern, using Android data binding.
I have an activity with my ViewModel object inside. I have also a RecyclerView adapter, which contains an arraylist of items that are converted to another Viewmodel. I want to save the state of the list inside the adapter (for rotation changes).
So: where should I save it? inside the activity? Or in the viewModel of the activity? Or somewhere else?
Also, there is another thing that isn't really clear.
Is it fine to perform rest calls (through Retrofit) or database calls directly from the viewModel (since i'd use interfaces), or would it be better to make an interface that the view (activity) implements and performs all the calls?
I will try to explain
First. So: where should I save it? inside the activity? Or in the viewModel of the activity? Or somewhere else?
and
Second. Is it fine to perform rest calls (through Retrofit) or database calls directly from the viewModel (since i'd use interfaces), or would it be better to make an interface that the view (activity) implements and performs all the calls?
on example from article.
On the schema below you can see implementation of OnClickListener and OnLongClickListener for RecyclerView item.
doted lines is a links
solid lines is a method calls
How it works
ViewHolderWrapper works as ClickListener for root view of ViewHolder. Depending on implementation ViewHolderWrapper can be proxy for HolderClickObservable or depending on SelectionHelper, ViewHolderWrapper can manually highlight selected item.
SelectionHelper is responsible for saving selection state and notify SelectionObserver about changes.
Listener (Adapter in this case) is responsible for highlighting of selected items and for updates.
Summarizing
First. You can restore adapter state via activity with methods onSaveInstanceState() and onRestoreInstanceState().
Second. You need to create lightweight ViewHolder which only responsible for binding data to view. Actions can be performed at adapter or activity.
See also example application

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

Multiple Activities / Fragments and the Model View Presenter pattern

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.

Categories

Resources