I am using RXJava on Android for asynchronously access the database.
I want to save an object in my database.
In this way, I created a method which take a final parameter (the object I want to save) and returns an Observable.
At this point I don't care to emit anything so I will call subscriber.onComplete() at the end.
Here is my code:
public Observable saveEventLog(#NonNull final EventLog eventLog) {
return Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(Subscriber<? super Object> subscriber) {
DBEventLog log = new DBEventLog(eventLog);
log.save();
subscriber.onCompleted();
}
});
}
The thing is, I saw many answer using the final keyword for the parameter, but I would like to do this without it.
The reason is I don't really like the approach of declare a final variable in order to use it in another thread.
Is there any alternative? Thanks.
We usually suggest avoiding the use of create because it may seem simple to use it but they usually violate the advanced requirements of RxJava. Instead, you should use one of the factory methods of Observable. In your case, the just factory method will get what you wanted: no final parameter:
public Observable<?> saveEventLog(#NonNull EventLog eventLog) {
return Observable
.just(eventLog)
.doOnNext(e -> {
DBEventLog log = new DBEventLog(e);
log.save();
})
.ignoreElements();
}
Related
The DarkSkyAPI call returns Forecast object containing WeeklyData object that in turn contains Array<DailyData>.
My Repository class requires Array<DailyData> to cache and propagate the data to the Presenter.
Currently I am calling the API like this:
Flowable<Forecast> response = service.getRxWeatherResponse(params...);.
How can I unwrap this Flowable<Forecast> to extract Flowable<Array<DailyData>> to be returned to the Repository class?
Thank you.
Got it Chris, thanks! I've used the map operator as you advised. Final code returns Observable and looks like this:
return service.getRxWeatherResponse(API cal params...)
.map(new Function<Forecast, List<DailyData>>() {
#Override
public List<DailyData> apply(Forecast forecast) throws Exception {
return forecast.getWeeklyData().getDailyData();
}
});
Or simplified using lambda:
return service.getRxWeatherResponse(API cal params...)
.map(forecast -> forecast.getWeeklyData().getDailyDataArray());
So, I have an Android app that uses realm.io. I have to run queries asynchronously like this :
public static void getProductsByCategoryId(Realm realm,
String categoryId,
OrderedRealmCollectionChangeListener<RealmResults<Product>> callback) {
RealmResults<Product> result = realm.where(Product.class)
.equalTo(CATEGORY, categoryId)
.findAllAsync();
result.addChangeListener(callback);
}
The callback will process this response, but then I need to run another query in sequence. So, you'll have queryA => process response => queryB => process response. So, the callback may have code like this
.....
getProductsByCategoryId(app.getRealmInstance(), "ABC123", firstCallback);
.....
private OrderedRealmCollectionChangeListener<RealmResults<Product>> firstCallback = new OrderedRealmCollectionChangeListener<RealmResults<Product>>() {
#Override
public void onChange(RealmResults<Product> realmProducts, OrderedCollectionChangeSet changeSet) {
mProdList.addAll(mRealm.copyFromRealm(realmProducts));
// get more product info (2nd call)
MainApplication.getMoreProductInfo(mRealm, mCatId, false, secondCallback);
}
};
Currently, my understanding is that you would run queryB in the callback of queryA ? Looking at the requirements for the app, I will end up with chains of 3 or 4 queries. Is this an appropriate approach, or is there a specific pattern I should be using ? I haven't found any guidance yet in the Realm documentation.
It's generally an indication of bad schema design if you need to do multiple queries in order to retrieve your result set, because the way Realm works is that if you can define your query results with one query (and you don't use realm.copyFromRealm() which you generally don't need to use anyways), then its elements and the results itself are all lazy-loaded.
If you cannot accomplish that, then even then, generally you probably shouldn't chain find*Async calls, because any RealmResults that you don't store as a field variable has a chance of being consumed by GC, and its change listener won't be called when isLoaded() is true (because said RealmResults no longer exists).
So what you really seem to want to do is just execute multiple queries on a background thread then return copied results to the main thread, in which case it'd just look like this
Executor executor = Executors.newSingleThreadedPool(); // or some other pool
Handler handler = new Handler(Looper.getMainLooper());
public void getQueryResults(DataLoadedCallback callback) {
executor.execute(() -> {
try(Realm realm = Realm.getDefaultInstance()) {
realm.refresh(); // <-- might not be necessary
RealmResults<XYZ> results1 = realm.where(XYZ.class)./*...*/.findAll();
RealmResults<ZXY> results2 = realm.where(ZXY.class)./*...*/.findAll();
RealmResults<YZX> results3 = realm.where(YZX.class)./*...*/.findAll();
List<Something> someList = new LinkedList<>();
for/*do magic transform things*/
someList.add(blah /* blah is not a managed RealmObject */);
}
handler.post(() -> {
callback.onDataLoaded(Collections.unmodifiableList(new ArrayList<>(someList)));
});
}
});
}
Chaining queries in the callbacks are fine and "should just work", but it would be far more efficient if you can express what you want is as few queries as possible.
Ideally, we should have a query language that is powerful enough to express everything you want in one query. We are not fully there yet, but we would be very interested to hear more about what specific requirements you have.
Also, it isn't clear why you are using copyFromRealm in the method you posted, but in an ideal situation that shouldn't be necessary.
I have one async-method, like this:
void getPlaceInfo(Place place, PlaceCallback callback)
For example my PlaceCallback has one method:
void success(InfoPlace place);
I want create Observable for waiting response from two requests:
getPlaceInfo(...); // first call
getPlaceInfo(...); // second call
And then I want to get both response at the same time.
Can I make it?
So you need to combine 2 callbacks to evaluate a function like:
Response computeResponse(InfoPlace infoPlace, InfoPlace infoPlace2) {
...
}
And you want to use Rx for this. There is two problem here; wrapping the callback method into Observable, and combine them.
You can't just instantiate an Observable and call myObservable.send(value) or myObservable.next(value). The first solution is to use a Subject. Another solution (the one below) is to create the observable with Observable.create(). You call the callback method and create the listener inside the method Observable.create(), because it's inside Observable.create() that you can call onSuccess() method, the method who told the Observable to pass down a value:
Single<InfoPlace> getInfoPlaceSingle(Place place) {
return Single.create(e -> getPlaceInfo(place, new PlaceCallback() {
#Override
public void success(InfoPlace infoPlace) {
e.onSuccess(infoPlace);
}
}));
}
Or with Java 8:
Single<InfoPlace> getInfoPlaceSingle(Place place) {
return Single.create(e -> getPlaceInfo(place, e::onSuccess));
}
Note, I used Single over Observable, since you await only one value. The interest is a more expressive contract for your function.
Wrapping things into Observable is a pain, but now you are in the reactive realm where Rx provide you a complete toolbox to deal with them. The second part - combining the observables - is easy:
Single.zip(getInfoPlaceSingle(place1), getInfoPlaceSingle(place2), this::computeResponse);
Wrap your async calls using Observable.fromEmitter() and you can then use Observable.zip() to combine the calls.
I was wondering about token authentication with Retrofit/RxJava.
I was refactoring my code to use a DataManager, such that the activity evokes a method in the presenter, the presenter subscribes to the datamanager.getTrips which is then responsible for the call to the api.
I want to make sure that my accesstoken is valid, and if it is not generate it first and then complete the task. Would doOnCompleted be a good way of achieving this or is there a better way?
/*In DataManager, service is the REST Interface*/
public Observable<VtTripResponseModel> getTrips(final String start, final String end, final String time, final String date){
if(accessToken.isValid()){
return service.getTrip(accessToken.getAccessString(),start,end,time,date,JSON_PARAM);
}
else{
generateAccessToken().doOnCompleted(new Action0() {
#Override
public void call() {
getTrips(start, end, time, date);
}
});
}
/*What do I return here? Since this will not be reached*/
}
To elaborate on #david.mihola 's answear you could do it like this:
Observable.just(accessToken.isValid())
.flatMap(valid -> {
if(valid){
return Observable.just(accessToken);
} else {
return generateAccessToken();
})
.flatMap(token -> service.getTrip(token.getAccessString(),start,end,time,date,JSON_PARAM))
So that the first flatMap generates token if it is not valid and if it is, then simply passes it on(this assumes that generateToken() returns Observable<AccessToken>). Second flatMap is just the thing that you wanted to do.
And to give some context to #MatBos's elaboration on my comment, especially with regard to your question
What do I return here? Since this will not be reached
It felt quite eye-opening for me when I realized that an Observable (or at least a cold-one, like the one we are talking about here, and the one that #MatBos described in his answer) is essentially a description of a future computation.
Returning an Observable just means that you return a blue-print for what should happen if and when someone (i. e. the code that called your getTrips method) actually subscribes to that Observable. Of course, an Observable is also an asynchronous stream of events, but I think that my description here is valid, too.
So, what do you return? A description that says:
If someone subscribes
1. First check if we have valid access token
2. a) If we do, just forward the access token for later use
b) If we don't, generate a new one access token and forward that
3. Take whatever access token you get - it is now guaranteed to be valid and use to retrieve the trips.
4. Forward them to the subscriber when they are ready.
And that description is exactly the Observable that #MatBos described.
Thank you for the input, In the meantime I was flying away and found a similar, but formulated in another way post: Retrofit with RxJava which had an answer in it.
My code now looks like:
/*In DataManager*/
public Observable<VtTripResponseModel> getTrips(String start, String end, String time, String date){
return service.getTrip(accessToken.getAccessString(),start,end,time,date,JSON_PARAM);
}
public Observable<VtResponseModel> getLocationByInput(String input){
return service.getLocationByInput(accessToken.getAccessString(),input,JSON_PARAM);
}
/*SF 26201420*/
public <T> Func1<Throwable,? extends Observable<? extends T>> refreshTokenAndRetry(final Observable<T> toBeResumed) {
return new Func1<Throwable, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Throwable throwable) {
// Here check if the error thrown really is a 401
if (isHttp401(throwable)) {
return service.getAccessToken(CREDENTIALS, DEVICE).flatMap(new Func1<AccessToken, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(AccessToken token) {
accessToken = token;
return toBeResumed;
}
});
}
// re-throw this error because it's not recoverable from here
return Observable.error(throwable);
}
};
}
And the method in my presenter now looks like
public void loadRepositories(String search){
subscription = manager.getLocationByInput(search)
.onErrorResumeNext(manager.refreshTokenAndRetry(Observable.defer(() -> manager.getLocationByInput(search))))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(application.defaultSubscribeScheduler())
.subscribe(new Subscriber<VtResponseModel>() {... etc}
Now when the first call is made after starting the application, it will generate an accesstoken since I recieve a 401. Then that accesstoken is stored within the manager, and reused until there is a new 401 (after it has expired).
I'm new to RXJava and i'm having trouble understanding how to chain together the result of API calls.
I'm making two API calls using retrofit, A and B, which both return an observable List of objects. Both API calls are independent so I want to make both at the same time, but to achieve my final result, I need to first take the result of A, do some work, then combine that with the result of B to populate my list adapter.
Make API Call A
Make API Call B
Take A's result and create result X
Take Result of B + X and populate adapter
#GET("/{object_id}/object_a")
Observable<List<Object_A>> getObjectAList(
#Path("object_id") long object_id);
#GET("/{object_id}/object_b")
Observable<List<Object_B>> getObjectBList(
#Path("object_id") long object_id);
This is where I get lost trying to use RX java. I can take the result of api call A and do my work
but I'm not sure how to take the result I just generated and combine it with API Call B.
aService. getObjectAList(object_a.getID())
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(new Action1<List<Object_A>>() {
#Override
public void call(List<Section> sections) {
// Do Stuff Here...
// Now i need to take this result and combine it with API Call B...
}
});
I want to make both API calls at the same time, but i'm not sure how to chain together and combine API calls. Any help is appreciative.
Something like this?
Observable
// make api call for A list and B list
.combineLatest(getObjectAList(), getObjectBList(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(List<Object_A> o, List<Object_B> o2) {
// Do what you need to do in the background thread
List x = createX(o);
List y = createY(x, o2);
return y;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Object y) {
// UI thread, do what you need e.g. renders the list
mAdapter.setList(y);
}
});
Taking care of replacing the proper types should bring you quite close to the solution.
The question is : how would you combine results ?
Building a new result from List and List ? Combine A objects with B objects ?
Answer to this question help to find the right operator for your problem.
A simple example of combining results can be this :
getObjectAList().zipWith(getObjectBList(), (aList, bList) -> // combine both list to build a new result)).subscribe()
You can combine elements of the list too with another operator (combineLatest for example)
aObs = getObjectAList().flatMap(Observable::from);
bObs = getObjectBList().flatMap(Observable::from);
Observable.combineLatest(aObs, bObs, (a,b) -> // combine a object with b object).subscribe();
For all of this examples above, requests will be done in parallel by retrofit.
I'd probably do something like the following
Observable convertedObservable = getObjectAList
.map(object_as -> convertAToX(object_as));
Observable.combineLatest(convertedObservable, getObjectBList, (listx, listb) -> {
return listx.addAll(listb);
}).subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.main)
.subscribe(r -> {
setAdapterWith(r);
});
Keep in mind this is using lambdas instead of anonymous classes but you should get the gist. Map is a great way of converting one object type to another (results of A to Results of X). So you can decide how convertAToX method works for you. Then you can use combineLastest on the converted A-X and B to return the list of R which updates your adapter
Ideally this is all in a ViewModel of some kind where getObjectAList and getObjectBList can me mocked on with Mock observables and you can test all the logic easily :)