I am messing around some with the google awareness api and now my understanding of RxJava is limiting me.
What I want to achieve in the end:
I want to get a Weather and a Location from the Api, and merge them into one object that I can pass on to my view for update.
However, I'm not sure how I achieve the returning of an Observable from the api callback here since it has void return type, and how to achieve merging of the weather and location object from api.getWeather and api.getLocation
public void requestUserCurrentInfo() {
Subscription userInfo = getWeatherLocation().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(userinfo ->
Log.d(TAG,userinfo.something()));
}
public Observable<UserInfo> getWeatherLocation () {
try {
Awareness.SnapshotApi.getWeather(client)
.setResultCallback(weather -> {
if (!weather.getStatus().isSuccess()) {
Log.d(TAG, "Could not get weather");
return;
}
//How do I do here?
return weather.getWeather();
});
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(retrievedLocation -> {
if(!retrievedLocation.getStatus().isSuccess()) return;
Log.d("FRAG", retrievedLocation.getLocation().getLatitude() + "");
});
} catch (SecurityException exception) {
throw new SecurityException("No permission " + exception);
}
}
For my other things in my Project, I get some stuff through a REST api following the repository pattern, then I can get it like this because every step returns a Observable< SmhiResponse >
getWeatherSubscription = getWeatherUsecase.execute().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(
smhiResponseModel -> {Log.d(TAG,"Retrieved weather"); locationView.hideLoading();},
err -> {Log.d(TAG,"Error fetching weather"); locationView.hideLoading();}
);
You don't return an observable from the callback but wrap your callbacks into observables to make them combinable (untested):
Observable<WeatherResult> weatherObservable = Observable.create(subscriber -> {
Awareness.SnapshotApi.getWeather(client)
.setResultCallback(weather -> {
if (!weather.getStatus().isSuccess()) {
subscriber.onError(new Exception("Could not get weather."));
Log.d(TAG, "Could not get weather");
} else {
//How do I do here?
subscriber.onNext(weather);
subscriber.onCompleted();
}
});
});
Observable<LocationResult> locationObservable = Observable.create(subscriber -> {
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(retrievedLocation -> {
if(!retrievedLocation.getStatus().isSuccess()) {
subscriber.onError(new Exception("Could not get location."));
} else {
Log.d("FRAG", retrievedLocation.getLocation().getLatitude() + "");
subscriber.onNext(retrievedLocation);
subscriber.onCompleted();
}
});
});
now combine them via .combineLatest() or .zip():
Observable<CombinedResult> combinedResults = Observable.zip(weatherObservable, locationObservable,
(weather, location) -> {
/* somehow combine weather and location then return as type "CombinedResult" */
});
don't forget to subscribe, otherwise none of them gets executed:
combinedResults.subscribe(combinedResult -> {/*do something with that stuff...*/});
Observable.combineLatest(getWeather (), getLocation(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(Object o, Object o2) {
combine both results and return the combine result to observer
}
})
getweather() and getlocation() return observables
Related
I'm using Single.fromCallable() to make a network call and I want to use the data from the response to make a second call.
How can I chain these two calls instead of nesting them?
private void queryForUser() {
Single.fromCallable(() -> remoteRepository.queryForUser()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response-> {
String username = response.getUsername();
//Perform second call
performSecondQuery(username);
}, err -> {
Log.e(TAG, "Failed to get user", err);
});
}
private void performSecondQuery(String username){
Single.fromCallable(() -> remoteRepository.performSecondQuery(username)))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(secondQueryResult -> {
Log.d(TAG, "performSecondQuery: " + secondQueryResult);
}, err -> {
Log.e(TAG, "Failed to perform second query", err);
});
}
My Java lambda syntax is rusty, but using flatMap() should give you something like this:
private Single<Whatever> performSecondQuery(String username){
return Single.fromCallable(() -> remoteRepository.performSecondQuery(username)))
}
private void queryForUser() {
Single.fromCallable(() -> remoteRepository.queryForUser()))
.flatMap { response -> performSecondQuery(response.getUsername()) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
// TODO something with the result
}, err -> {
Log.e(TAG, "Failed to get user", err);
});
}
(note: I don't know what the second query is doing, so I don't know what Whatever is — it would be the return type of remoteRepository.performSecondQuery())
I'm trying to find a way to handle asynchronous network requests in Swift. My plan is to isolate these calls in their own class, so I need a way to handle callbacks. In Android, I'm doing something like the following using an Interface:
apiService.fetch(data, new Callback() {
#Override
public void onResponse(Response r) {
// success
}
#Override
public void onFailure(Error e) {
// error
}
});
Can someone point me in the direction on how I can handle this similarly in Swift 3? Thanks!
It's possible to have multiple completion handlers. Here is a short example:
func request(url:String, success:#escaping(Any) -> Void, failure:#escaping(Any?) -> Void)
{
let task = URLSession.shared.dataTask(with: URL.init(string: url)!) { (data, response, error) in
if let responseError = error
{
failure(responseError)
}
else if let responseData = data //Make additional checks if there is an error
{
success(responseData) //Call when you are sure that there is no error
}
else
{
failure(nil)
}
}
task.resume()
}
Example of usage:
self.request(url: "https://jsonplaceholder.typicode.com/posts", success: { (data) in
//Do if success
}) { (error) in
//Do if error
}
i would like to implement a Pollingservice which calls a REST Api every nDelay Seconds and notify all subscribers if the data has been changed. Now i have a little problem with my code since it always returns a value to my Consumer, even if the data has not been changed.
private Observable<List<HueLight>> pollingLightsObservable = null;
public Observable<List<HueLight>> getPollingLightsObservable() {
if (pollingLightsObservable == null) {
pollingLightsObservable = Observable.fromCallable(
() -> LightManager
.getInstance(context)
.getLights()
.blockingSingle())
// .distinctUntilChanged( (l1, l1) -> !l1.equals(l2) )
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1, TimeUnit.SECONDS)));
}
return pollingLightsObservable;
}
Enabling or using the distinctUntilChanged dont change anything. Doesnt matter if i put it before or after my repeatWhen.
Since my RetroFit Call returns an Observable, i have to use blockingSingle(). Using the Observable directly it leads into a return of "4, 8, 12, 16, .." items with this sample:
LightManager.getInstance(context).getLights()
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1, TimeUnit.SECONDS)))
Currently i subscribe from different classes/activites with
this.lightChangeSubscriber = PollingManager
.getInstance(getContext())
.getPollingLightsObservable()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(hueLights -> {
{
Log.d(TAG, "Lights received successfully! Size=" + hueLights.size());
}
});
I would lovely avoid using interfaces and timer to create the polling. What would you recommend ?
what about using some custom filter?
public class FilterDuplicateHueConfig implements Predicate<HueConfig> {
private HueConfig lastVal;
#Override
public boolean test(HueConfig newVal) {
if(lastVal == null) {
lastVal = newVal;
return true;
}
... compare here the two values and return true/false appropriately...
}
}
I want to perform long calculations on background thread using RXJava in android. After calculation I am trying to present the result in Recylerview. I am using following piece of code:
Observable.just("true")
.subscribeOn(Schedulers.io())
.map(new Func1<String, String>() {
#Override
public String call(String s) {
feedlist.clear();
if (eventFeedItems != null && !eventFeedItems.isEmpty()) {
for (int i = 0; i < eventFeedItems.size(); i++) {
if (eventFeedItems != null && eventFeedItems.get(i) != null
&& ((eventFeedItems.get(i).getType() != null && eventFeedItems.get(i).getType().equalsIgnoreCase("EVENT"))
|| (eventFeedItems.get(i).getActivityRequestType() != null && eventFeedItems.get(i).getActivityRequestType().equalsIgnoreCase(EventConstants.TRENDING_ACTIVITY)))) {
if (eventFeedItems.get(i).getActivityRequestType() != null && !eventFeedItems.get(i).getActivityRequestType().equalsIgnoreCase("")) {
feedlist.add(new FeedsListModel(eventFeedItems.get(i), eventFeedItems.get(i).getActivityRequestType(), null));
} else if (eventFeedItems.get(i).getRequestType() != null && !eventFeedItems.get(i).getRequestType().equalsIgnoreCase("")) {
feedlist.add(new FeedsListModel(eventFeedItems.get(i), eventFeedItems.get(i).getRequestType(), null));
} else
feedlist.add(new FeedsListModel(eventFeedItems.get(i), EventConstants.ATTENDEE_POST, null));
}
}
}
Log.d("calculations","Completed");
return "";
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
// feed_list.setLayoutManager(mLayoutManager);
// feedListAdapter.notifyDataSetChanged();
Log.d("Adapter", "Set");
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.d("Exception", "oh! fish...");
throwable.printStackTrace();
}
});
With the above piece of code I am facing UI Hindring as the ArrayList eventFeedItems size is of about more then 300 items.I am new to RXJava. Please help me out.
you will not achieve concurrency using map-Operator.
The first subscribeOn will move all emitions to IO-scheduler. No concurrency happening here.
.subscribeOn(Schedulers.io())
The map-operator will be called synchronously from previous thread. In your case it would be some thread from IO-threadpool.
.map(new Func1<String, String>() {
After the map-Operator has been executed, you will move the value from the IO-thread to the Android-UI-event-loop with
.observeOn(AndroidSchedulers.mainThread())
After the value has been translated from IO-thread to the UI-thread the next value from the initial observable will be processed.
Observable.just("true")
In your example there will be no more values because you only produce one value.
In order to achieve concurrency you should use flatMap instead of map. And use subscribeOn() in the flatMap to create each stream on another thread.
Please consider this example, to see how concurrency is happening. Every observable will be subscribed at once, so the max. time for the teset would be something around 5 seconds. If now concurrency would happen it would take 1+2+3+4+5 seconds plus execution time.
#Test
public void name1() throws Exception {
Observable<Integer> value = Observable.just(1_000, 2_000, 3_000, 4_000, 5_000)
.flatMap(i -> Observable.fromCallable(() -> doWork(i)).subscribeOn(Schedulers.io())
).doOnNext(integer -> System.out.println("value"));
value.test().awaitTerminalEvent();
}
private int doWork(int sleepMilli) {
try {
Thread.sleep(sleepMilli);
} catch (InterruptedException e) {
e.printStackTrace();
}
return -1;
}
If you want to know more about how concurrency is happening with flatMap please consider reading http://tomstechnicalblog.blogspot.de/2015/11/rxjava-achieving-parallelization.html
In regard to your code I would suggest:
Move the anonymouse implementation of interface to a private inner class implementation and use an instance of it. You will get a more readable observable
Don't use side-effects to global variables from operators within. You
will get race-condition if concurrency is involved.
List<FeedsListModel> eventFeedItems = Arrays.asList(new FeedsListModel(), new FeedsListModel());
Observable<FeedsListModel> feedsListModelObservable = Observable.fromIterable(eventFeedItems)
.flatMap(feedsListModel -> Observable.fromCallable(() -> calculation(feedsListModel))
.subscribeOn(Schedulers.computation())
);
feedsListModelObservable
.toList()
.observeOn(Schedulers.io())
.subscribe(feedsListModels -> {
// do UI stuff
});
Helping-method:
private FeedsListModel calculation(FeedsListModel model) {
// do calculation here
return new FeedsListModel();
}
I have an observable which might fail with a special exception in which case I want to show a dialog with a retry button. I've seen this answer, but it doesn't quite do what I want. I wasn't able to use retryWhen to solve my problem, so instead I used onErrorResumeNext. If you can come up with a way to do the same with retryWhen, please tell.
Right now I have this piece of code:
public Observable<Order> proceedWithOrdering(Activity activity) {
return apiService.createOrder()
.subscribeOn(Schedulers.io())
.compose(applyRetryLogic(activity))
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread());
}
public <T extends ApiResponse> Observable.Transformer<T, T> applyRetryLogic(Activity activity) {
return observable -> observable
.onErrorResumeNext(retry(observable, activity))
.subscribeOn(AndroidSchedulers.mainThread());
}
public <T> Func1<Throwable, ? extends Observable<? extends T>> retry(Observable toRetry, Activity activity) {
return throwable -> {
if (throwable instanceof NetworkException) {
MaterialDialog dialog = retryDialog(activity);
View retry = dialog.getActionButton(DialogAction.POSITIVE);
View cancel = dialog.getActionButton(DialogAction.NEGATIVE);
Observable<Object> retryClick = RxView.clicks(retry).map(o -> {
dialog.dismiss();
return o;
});
Observable<Object> cancelClick = RxView.clicks(cancel).flatMap(o -> {
dialog.dismiss();
return Observable.error(throwable);
});
dialog.show();
return Observable.amb(retryClick, cancelClick)
.flatMap(o -> toRetry.compose(applyRetryLogic(activity)));
} else {
return Observable.error(throwable);
}
};
}
The problem is that the call inside the retry gets executed not on the main thread and it raises the Can't create handler inside thread that has not called Looper.prepare() exception.
The question is - how do I force it to be executed on the main thread? As you can see, I have already tried doing subscribeOn(AndroidSchedulers.mainThread()) right after both compose and onErrorResumeNext with no luck.
I have tested my code using simple observables that don't operate on separate threads and it works fine.
You can accomplish this by flatMapping a PublishSubject which is then updated once the relevant button is pressed. Here is a classical Java Swing example.
public class RetryWhenEnter {
public static void main(String[] args) {
AtomicInteger d = new AtomicInteger();
Observable<Integer> source = Observable.just(1);
source.flatMap(v -> {
if (d.incrementAndGet() < 3) {
return Observable.error(new RuntimeException());
}
return Observable.just(v);
})
.retryWhen(err -> {
return err.flatMap(e -> {
System.out.println(Thread.currentThread() + " Error!");
PublishSubject<Integer> choice = PublishSubject.create();
SwingUtilities.invokeLater(() -> {
int c = JOptionPane.showConfirmDialog(null,
e.toString() + "\r\nRetry?", "Error",
JOptionPane.YES_NO_OPTION);
if (c == JOptionPane.YES_OPTION) {
choice.onNext(1);
} else {
choice.onCompleted();
}
});
return choice;
});
}).subscribe(System.out::println,
Throwable::printStackTrace);
}
}
Edit:
Or use observeOn(AndroidSchedulers.mainThread()) just before onErrorResumeNext or when using retryWhen: retryWhen(o -> o.observeOn(AndroidSchedulers.mainThread())...).
Edit 2
I've rolled back the change so the answer is meaningful again.
There is a way of solving my problem using retryWhen (thanks #akarnokd):
public <T extends ApiResponse> Observable.Transformer<T, T> applyRetryLogic(Activity activity) {
return observable -> observable
.retryWhen(err -> err.flatMap(throwable -> {
L.d(Thread.currentThread() + " Error!");
if (throwable instanceof NetworkException) {
PublishSubject<Integer> choice = PublishSubject.create();
activity.runOnUiThread(() -> {
MaterialDialog dialog = retryDialog(activity);
View retry = dialog.getActionButton(DialogAction.POSITIVE);
View cancel = dialog.getActionButton(DialogAction.NEGATIVE);
RxView.clicks(retry).subscribe(o -> {
dialog.dismiss();
choice.onNext(1);
});
RxView.clicks(cancel).subscribe(o -> {
dialog.dismiss();
choice.onError(throwable);
});
dialog.show();
});
return choice;
} else {
return Observable.error(throwable);
}
}));
}