In my application I am accessing my business objects using a Repository and RxJava. From my Presenter layer I can ask the Repository to get me ObjectA or get me ObjectB and the Respository will return an Observable or Observable respectively.
Sometimes I have the need to get both ObjectA and ObjectB. I'm wondering what the options are for fetching both objects using RxJava that also allows unit testing of my Presenter layer.
The way I originally implemented this was to use the flatMap() operator. So I'd do something like this:
//In Presenter layer
Observable<ObjectA> obsA = repository.getObjectA();
Observable<ObjectB> obsB = repository.getObjectB();
Observable<ObjectB> obsResult = obsA.flatMap(new Func1<ObjectA, Observable<ObjectB>>() {
#Override
public Observable<ObjectB> call(ObjectA a) {
mObjectA = a;
return obsB;
}
});
When I subscribe to the obsResult, obsA runs and I can access its result in the flatMap() operator. I get a handle to its result and store it as a field in my Presenter. Then obsB runs and I get its result in my Subscriber. This works just fine but I can't help but think I'm not really doing it right. One issue is that although I may have 100% test coverage in my Repository class, now I'm manipulating the observables that come out of the Repository class and I'm having a hard time figuring out how to test the obsResult or to verify that the code I write in flatMap() is correct.
Should I be adding methods to my Repository layer such as getObjectAandObjectB and return an Observable and do all of the work in the Repository layer? This would allow me to test the resultant Observable in the Repository layer tests rather than trying to create a new Observable from two separate Observables and figure out how to test it in my Presenter layer.
Another thing I've looked at is using the zip() operator. This would look something like this:
Observable<ObjectA> obsA = repository.getObjectA();
Observable<ObjectB> obsB = repository.getObjectB();
Observable<CombinedObjectAandObjectB> resultObs = obsA.zipWith(obsB, new Func2<ObjectA, ObjectB, CombinedObjectAandObjectB>() {
#Override
public Object call(ObjectA a, ObjectB b) {
return new CombinedObjectAandObjectB(a,b);
}
});
Now when I subscribe to my resultObs I get a composite object returned with both ObjectA and Object B. This is pretty slick but still requires me to write code in Func2 that needs to be tested.
What are some ways that I can achieve my goals of combining calls for my business objects while also allowing for testability of the resultant Observable?
You almost anwser your question yourself. According to Clean Architecture it's better to pass ready-to-use models to presentation layer. Combining logic belongs to Domain layer. So use zip() in your Repository (although it's Data layer). E.g.:
Observable.zip(modelAObservable, modelBOsbervable, (A, B) -> {
combineModels(A, B);
})...
private CombinedModel combineModels(modelA, modelB) {
//combining logic
}
And test it as usual. E.g.:
ModelA A = new ModelA...
ModelB B = new ModelB...
CombinedModel expectedResult = new CombinedModel...
assertEquals(combineModels(A, B), expectedResult);
Related
The company I just started working at uses a so called Navigator, which I for now interpreted as a stateless ViewModel. My Navigator receives some usecases, with each contains 1 suspend function. The result of any of those usecases could end up in a single LiveData. The Navigator has no coroutine scope, so I pass the responsibility of scoping suspending to the Fragment using fetchValue().
Most current code in project has LiveData in the data layer, which I tried not to. Because of that, their livedata is linked from view to dao.
My simplified classes:
class MyFeatureNavigator(
getUrl1: getUrl1UseCase,
getUrl1: getUrl1UseCase
) {
val url = MediatorLiveData<String>()
fun goToUrl1() {
url.fetchValue { getUrl1() }
}
fun goToUrl2() {
url.fetchValue { getUrl2() }
}
fun <T> MediatorLiveData<T>.fetchValue(provideValue: suspend () -> T) {
val liveData = liveData { emit(provideValue()) }
addSource(liveData) {
removeSource(liveData)
value = it
}
}
}
class MyFeatureFragment : Fragment {
val viewModel: MyFeatureViewModel by viewModel()
val navigator: MyFeatureNavigator by inject()
fun onViewCreated() {
button.setOnClickListener { navigator.goToUrl1() }
navigator.url.observe(viewLifecycleOwner, Observer { url ->
openUrl(url)
})
}
}
My two questions:
Is fetchValue() a good way to link a suspend function to LiveData? Could it leak? Any other concerns?
My main reason to only use coroutines (and flow) in the data layer, is 'because Google said so'. What's a better reason for this? And: what's the best trade off in being consistent with the project and current good coding practices?
Is fetchValue() a good way to link a suspend function to LiveData?
Could it leak? Any other concerns?
Generally it should work. You probably should remove the previous source of the MediatorLiveData before adding new one, otherwise if you get two calls to fetchValue in a row, the first url can be slower to fetch, so it will come later and win.
I don't see any other correctness concerns, but this code is pretty complicated, creates a couple of intermediate objects and generally difficult to read.
My main reason to only use coroutines (and flow) in the data layer,
is 'because Google said so'. What's a better reason for this?
Google has provided a lot of useful extensions to use coroutines in the UI layer, e.g. take a look at this page. So obviously they encourage people to use it.
Probably you mean the recommendation to use LiveData instead of the Flow in the UI layer. That's not a strict rule and it has one reason: LiveData is a value holder, it keeps its value and provides it immediately to new subscribers without doing any work. That's particularly useful in the UI/ViewModel layer - when a configuration change happens and activity/fragment is recreated, the newly created activity/fragment uses the same view model, subscribes to the same LiveData and receives the value at no cost.
At the same time Flow is 'cold' and if you expose a flow from your view model, each reconfiguration will trigger a new flow collection and the flow will be to execute from scratch.
So e.g. if you fetch data from db or network, LiveData will just provide the last value to new subscriber and Flow will execute the costly db/network operation again.
So as I said there is no strict rule, it depends on the particular use-case. Also I find it very useful to use Flow in view models - it provides a lot of operators and makes the code clean and concise. But than I convert it to a LiveData with help of extensions like asLiveData() and expose this LiveData to the UI. This way I get best from both words - LiveData catches value between reconfigurations and Flow makes the code of view models nice and clean.
Also you can use latest StateFlow and SharedFlow often they also can help to overcome the mentioned Flow issue in the UI layer.
Back to your code, I would implement it like this:
class MyFeatureNavigator(
getUrl1: getUrl1UseCase,
getUrl1: getUrl1UseCase
) {
private val currentUseCase = MutableStateFlow<UseCase?>(null)
val url = currentUseCase.filterNotNull().mapLatest { source -> source.getData()}.asLiveData()
fun goToUrl1() {
currentUseCase.value = getUrl1
}
fun goToUrl2() {
currentUseCase.value = getUrl2
}
}
This way there are no race conditions to care about and code is clean.
And: what's the best trade off in being consistent with the project
and current good coding practices?
That's an arguable question and it should be primarily team decision. In most projects I participated we adopted this rule: when fixing bugs, doing maintenance of existing code, one should follow the same style. When doing big refactoring/implementing new features one should use latest practices adopted by the team.
I am using RxJava in my Android project and I'm happy about it. I'm currently using it to make all my DAO methods asynchronous and make UI listens on them.
But I have a big problem, that is, when I retrieve some data from database using Observable<List<User>> getLists(), I need to use List<User> in my ViewModels, but I cannot extract it from the observable.
I would like to know what is the common approach to solve this kind of problem ? I searched on Internet and people said it's not recommended to extract the objects, but in this case how can I use the data from database and at the same time still enable the observers listening ?
Should I create another method using AsyncTask ??
Thanks.
In my UserRepo.java
public Observable<List<User>> getUsers() {
return colisDao.getUsers();
}
In HomeScreenViewModel.java:
public List<User> getUsers() {
return userRepo.getUsers(); // do not work because I need a List<User>
}
In HomeActivity.java:
UserListAdapter userListAdapter = new UserListAdapter(this,
vm.getUsers());
Central idea of reactive extensions is to make use of events' streams observation and timely processing.
So actually, if you need to retrieve data in a straightforward way, I'd say you don't need RxJava2 at all. Still, if you want to stick to the reactive approach, the data stream should be listened to instead.
All RxJava2 types provide a subscribe method that "notifies" the source of data that's lazy by nature that here is an observer that wants to receive the data, so all the data processing flow described by use of RxJava2 operators will become alive.
The most painless approach is to change HomeActivity's code to this:
vm.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(userListAdapter::populateWithNewDataSet);
, assuming that adapter will have the mentioned method that will update the UI data set using something like notifyDataSetChanged() (or DiffUtil, for instance) internally.
By doing that the data source is now observed and every time the update is emitted the UI will be repopulated with the most recent data.
P.S.: I've just demonstrated the simplest way to do the thing, but it is up to the developer where to place RxJava-related code: be it ViewModel, Activity, or even some other component. RxJava is a convenient tool to use and it can make complicated asynchronous flow simple, but the problem with RxJava arises when all the code base is dependent on it. The code base can then quickly become unmanageable, fragile and rigid if the tool was used in an improper place.
Adding on #AndreyIlyunin very good answer, You could also use MutableLivedata in your Viewmodel to save the List in the viewmodel as Livedata and observe changes to it in your Activity. This is suggested by Google as a way to maintain MVVM architecture. Something like:
In HomeScreenViewModel.java:
private final MutableLivedata<List<User>> users = new MutableLivedata<>();
public void getUsers() {
return userRepo.getUsers()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onUsers)
}
private void onUsers(List<> list){
users.setValue(list);
}
public MutableLivedata<List<User>> getUserList(){
return users;
}
In HomeActivity.java, in onCreate() add:
vm.getUserList().observe(this,this::onUserList);
and add following methods to activity:
private void onUserList(List<> list){
userListAdapter = new UserListAdapter(this,list);
}
and then from your activity call:
vm.getUsers();
The getUsers() call is made asynchronously in the background, and you get the userList reactivly.
I'm coding an Android App, using RxJava, Retrofit and the Clean Architecture. The thing is that I have an Interactor/UseCase that needs to return the collection of favorite teams and favorite players. The collection of favorite teams is the results from one endpoint mixed with another. Same thing for the favorite players. In the Interactor/UseCase implementation I'm doing something like this:
getTeams = Observable.merge(mNetworkRetrofitService.getAllTeams(), mNetworkRetrofitService.getAllFavoriteTeams());
getPlayers = Observable.merge(mNetworkRetrofitService.getAllPlayers(), mNetworkRetrofitService.getAllFavoritePlayers());
Then I'm zipping this two responses together returning them in a single wrapper object:
Observable.zip(getTeams, getPlayers, new Func2<List<Team>, List<Player>, Pair<List<Team>, List<Player>>>() {
#Override
public Pair<List<Team>, List<Player>> call(List<Team> teamList, List<Player> players) {
return new Pair<>(teamList, players);
}
}).subscribe(new DefaultSubscriber<Pair<List<Team>, List<Player>>>() {
#Override
public void onNext(Pair<List<Team>, List<Player>> pair) {
callback.showResultOnUI(pair);
}
});
I know that the .zip() method is supposed to wait until the two rx.Observables end emitting the items, but in this case the mixing method is being executed more than one time.
My question is; Is there a way to keep the zip's merging method from being executed more than once?
Keep in mind this things:
I simplified the example. I'm actually also merging a cache rx.Observable with the Retrofit's rx.Observables.
The endpoints can't change. The wrapper object can't change. The response must be returned to te UI in a sigle object wrapper. We can't send the favorite teams and the favorite players in two different callback methods.
So, your zip method gets called multiple times because you act on List<T>s. This means that this Observable:
getTeams = Observable.merge(
mNetworkRetrofitService.getAllTeams(),
mNetworkRetrofitService.getAllFavoriteTeams());
Will have 2 items, each one a list - it won't merge them all together. What you want is to merge both lists into one:
getTeams = Observable.merge(
mNetworkRetrofitService.getAllTeams(),
mNetworkRetrofitService.getAllFavoriteTeams())
.flatMap(Observable::fromIterable()
.toList();
If you do this for both of your zip parameters, then you will get only one emission.
A bit of context, I’ve tried to apply some clean-architecture to one of my projects and I’m having trouble with the (Realm) disk implementation of my repository. I have a Repository which pulls some data from different DataStores depending on some conditions (cache). This is the theory, the problem comes when mixing all of this with UseCases and RxJava2.
First I get the list of objects from Realm and then I manually create an Observable of it. But the subscribe (as expected) is executed on a different thread so realm ends up crashing… (second block of code)
This is the code I use to create the Observables (from an abstract class DiskStoreBase):
Observable<List<T>> createListFrom(final List<T> list) {
return Observable.create(new ObservableOnSubscribe<List<T>>() {
#Override
public void subscribe(ObservableEmitter<List<T>> emitter) throws Exception {
if (list != null) {
emitter.onNext(list);
emitter.onComplete();
} else {
emitter.onError(new ExceptionCacheNotFound());
}
}
});
}
How can I deal with this scenario?
More code of DiskStoreForZone:
#Override
public Observable<List<ResponseZone>> entityList() {
Realm realm = Realm.getDefaultInstance();
List<ResponseZone> result = realm.where(ResponseZone.class).findAll();
return createListFrom(result);
}
The exact crash:
E/REALM_JNI: jni: ThrowingException 8, Realm accessed from incorrect thread.
E/REALM_JNI: Exception has been thrown: Realm accessed from incorrect thread.
It doesn't work because despite using Rx, your data layer is not reactive.
Realm by its nature is a reactive datasource, and its managed objects by nature are also mutable (updated in place by Realm), and thread-confined (can only be accessed on the same thread where the Realm was opened).
For your code to work, you'd need to copy out the data from the Realm.
#Override
public Single<List<ResponseZone>> entityList() {
return Single.fromCallable(() -> {
try(Realm realm = Realm.getDefaultInstance()) {
return realm.copyFromRealm(realm.where(ResponseZone.class).findAll());
}
});
}
I took the liberty and represented your Single as a Single, considering it's not an Observable, it does not listen for changes, there is only 1 event and that is the list itself. So sending it through an ObservableEmitter doesn't really make sense as it does not emit events.
Therefore, this is why I said: your data layer is not reactive. You are not listening for changes. You are just obtaining data directly, and you are never notified of any change; despite using Rx.
I drew some pictures in paint to illustrate my point. (blue means side-effects)
in your case, you call a one-off operation to retrieve the data from multiple data-sources (cache, local, remote). Once you obtain it, you don't listen for changes; technically if you edit the data in one place and another place, the only way to update is by "forcing the cache to retrieve the new data manually"; for which you must know that you modified the data somewhere else. For which you need a way to either directly call a callback, or send a message/event - a notification for change.
So in a way, you must create a cache invalidation notification event. And if you listen to that, the solution could be reactive again. Except you're doing this manually.
----------------------------------------------------------------------
Considering Realm is already a reactive data source (similarly to SQLBrite for SQLite), it is able to provide change notifications by which you can "invalidate your cache".
In fact, if your local data source is the only source of data, and any write from network is a change that you listen to, then your "cache" can be written down as replay(1).publish().refCount() (replay latest data for new subscribers, replace data with new if new data is evaluated) which is RxReplayingShare.
Using a Scheduler created from the looper of a handler thread, you can listen to changes in the Realm on a background thread, creating a reactive data source that returns up-to-date unmanaged copies that you can pass between threads (although mapping directly to immutable domain models is preferred to copyFromRealm() if you choose this route - the route being clean architecture).
return io.reactivex.Observable.create(new ObservableOnSubscribe<List<ResponseZone>>() {
#Override
public void subscribe(ObservableEmitter<List<ResponseZone>> emitter)
throws Exception {
final Realm observableRealm = Realm.getDefaultInstance();
final RealmResults<ResponseZone> results = observableRealm.where(ResponseZone.class).findAllAsync();
final RealmChangeListener<RealmResults<ResponseZone>> listener = results -> {
if(!emitter.isDisposed()) {
if(results.isValid() && results.isLoaded()) {
emitter.onNext(observableRealm.copyFromRealm(results));
}
}
};
emitter.setDisposable(Disposables.fromRunnable(() -> {
if(results.isValid()) {
results.removeChangeListener(listener);
}
observableRealm.close();
}));
results.addChangeListener(listener);
// initial value will be handled by async query
}
}).subscribeOn(looperScheduler).unsubscribeOn(looperScheduler);
Where looper scheduler is obtained as
handlerThread = new HandlerThread("LOOPER_SCHEDULER");
handlerThread.start();
synchronized(handlerThread) {
looperScheduler = AndroidSchedulers.from(handlerThread.getLooper());
}
And that is how you create reactive clean architecture using Realm.
ADDED:
The LooperScheduler is only needed if you intend to actually enforce Clean Architecture on Realm. This is because Realm by default encourages you to use your data objects as domain models and as a benefit provides lazy-loaded thread-local views that mutate in place when updated; but Clean Architecture says you should use immutable domain models instead (independent from your data layer). So if you want to create reactive clean architecture where you copy from Realm on a background thread any time when Realm changes, then you'll need a looper scheduler (or observe on a background thread, but do the copying from a refreshed Realm on Schedulers.io()).
With Realm, generally you'd want to use RealmObjects as your domain models, and rely on lazy-evaluation. In that case, you do not use copyFromRealm() and you don't map the RealmResults to something else; but you can expose it as a Flowable or a LiveData.
You can read related stuff about this here.
I've been adapting my Android app to use RxJava but I'm having a little bit of trouble doing so. As I had been advised in a previous post (Wait for all requests in Android Volley), I'm using Observables to mimic how I'm interfacing with my REST API in JavaScript. Specifically, using the promise library, I compose calls like this:
$q.all([
fetchResourceA(),
fetchResourceB()
])
.then(function (responses) {
...
return fetchResourceC();
})
.then(function (response) {
...
});
In this example, I query two resources simultaneously, collect the results, then collect a third resource based on some of the parameters from the previously collected resources. The best I've been able to do to mimic this in RxJava is like this:
Observable o = Observable.zip(
fetchResourceA(),
fetchResourceB(),
new Func2<ResA, ResB, Object>() {
#Override
public Object call(ResA resA, ResB resB) {
...
}
}
);
But I'm struggling to compose them like I did in JavaScript. Do I need to simply create a second observable and subscribe to it in the callback of the zip? That's what I'm doing now, and it works, but I'd like to know if there's a more elegant and more reactive-appropriate way to structure my requests.
The .then method from promise can be transposed to flatMap method in RxJava
So, what you can do, is to zip then flatMap then flatMap
Observable.zip(fetchA(), fetchB(), (a, b) -> new Response(a, b))
.flatMap((responses) -> fetchC())
.flatMap((cResponse) -> /* whatever */)
.subscribe();
Please note that fetchA(), fetchB(), fetchC() return Observables.
(My example use lambdas for clarity)