I have a Retrofit request API call with RxJava. All works well.
How I can optimise this code. I'm a new one with RxJava.
I would be grateful for a small example code or link.
Thanks.
Observable<Review> observer = Observable.just(review);
observer.subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.newThread())
.map(new Func1<Review, Uri>() {
#Override
public Uri call(Review review) {
Uri uri = null;
/// Some code
return uri;
}
}).subscribe(new Action1<Uri>() {
#Override
public void call(Uri uri) {
if(uri != null) {
Api.Reviews reviewApi = retrofit.create(Api.Reviews.class);
Observable<BaseResponse<Review>> reviews = reviewApi.createReview(authToken, review);
reviews.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(new Func1<BaseResponse<Review>, Void>(){
#Override
public Void call(BaseResponse<Review> response) {
//Code Here
return null;
}
}).subscribe();
}
}
});
There are a few options: map, doOnNext, flatMap, etc., depending on what do you want to do with the retrofit result afterwards. For example:
reviews
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map(r -> {
try {
someDB.save(r);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return r;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> { }, e -> { log(e); });
or
reviews
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.doOnNext(r -> {
try {
someDB.save(r);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> { }, e -> { log(e); });
or
reviews
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap(r -> {
try {
someDB.save(r);
return Observable.empty(); // or Observable.just(r);
} catch (Exception ex) {
return Observable.error(ex);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> { }, e -> { log(e); });
Recently I've published some examples of RxJava usage (https://github.com/dawidgdanski/rx-java-handies). If you find it useful while investigating the API for your cases I'll be glad to help you out.
Related
i have 4 observable, and i want call it sequence
Observable1 //return boolean
-> Observable2 // if(Observable1 result == true) { call Observable3 } else { call observable4 }
-> Observable3 // call Observable4
-> Observable4
-> subscribe();
i tried it, but when Observable1 result is false onCompleted called
Observable
.fromCallable(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
return false;
}
})
.flatMap(new Func1<Boolean, Observable<String>>() {
#Override
public Observable<String> call(Boolean aBoolean) {
if(aBoolean) {
return Observable.just("to Observable2");
}
return null; // to Observable 3
}
})
.flatMap(new Func1<String, Observable<Integer>>() {
#Override
public Observable<Integer> call(String s) {
return Observable.just(1); //to Observable3
}
})
.flatMap(new Func1<Integer, Observable<Boolean>>() {
#Override
public Observable<Boolean> call(Integer integer) {
return Observable.just(true); //success
}
})
.subscribe(new Subscriber<Boolean>() {
#Override
public void onCompleted() {
Log.e("TAG", "onCompleted");
}
#Override
public void onError(Throwable e) {
Log.e("TAG", "onError", e);
}
#Override
public void onNext(Boolean bool) {
Log.e("TAG", "onNext");
}
});
how can achieve to this ?
thanks
There are many ways of doing this. Only proposed this based on the first pseudocode you are offering.
Observable.just(boolean)
.flatmap( boolean -> if (boolean) return Observable3
else return Observable.just(boolean))
.flatmap(boolean -> Observable4)
.subscribe()
i solved this problem with define observables as functions
public void run() {
Observable
.fromCallable(() -> false)
.flatMap(bool -> {
if(bool) {
return getObservable2();
}
return getObservable3();
})
.subscribe(bool -> {
Log.e("TAG", "onNext");
}, e -> {
Log.e("TAG", "onError", e);
}, () -> {
Log.e("TAG", "onCompleted");
});
}
private Observable<Boolean> getObservable2() {
return Observable
.just("test")
.flatMap(s -> getObservable4());
}
private Observable<Boolean> getObservable3() {
return Observable
.just(1)
.flatMap(s -> getObservable4());
}
private Observable<Boolean> getObservable4() {
return Observable.just(true);
}
Observable.just(boolean)
.flatmap( boolean -> if (boolean)
return Observable3.flatmap(observable4)
else return Observable4))
.subscribe()
if the result of first observable is true than obervablr3 is called whose result is then flat mapped into the observable4 second case is if the result of first observable is false then it will call observable4 directly and the result is subscribes in first observable only
This is how I would attempt to solve your problem but it doesn't use flatmap.
Observable2.subscribe( aBoolean -> if(aBoolean){
Observable3.subscribe();
} else {
Observable2.subscribe( result -> Observable3.subscribe());
}
You should never return null from an Observable
I have the following methods
Document createDocument(String url);
List<MediaContent> getVideo(Document doc);
List<MediaContent> getImages(Document doc);
List< MediaContent> will be consumed by
void appendToRv(List<MediaContent> media);
I like to use RxJava2 such that
CreateDocument -> getVideo ->
-> appendToRv
-> getImages ->
(also, the video output should be ordered before images).
How would I go about doing that? I tried flatMap, but it seems to only allow a single method to be used
Single<List<MediaContent>> single =
Single.fromCallable(() -> createDocument(url))
// . ?? ..
// this is the part i am lost with
// how do i feed document to -> getVideo() and getImage()
// and then merge them back into the subscriber
//
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
single.subscribe(parseImageSubscription);
The DisposableSingleObserver
parseImageSubscription = new DisposableSingleObserver<List<MediaContent>>() {
#Override
public void onSuccess(List<MediaContent> media) {
if(media!=null) {
appendToRv(media);
}
}
#Override
public void onError(Throwable error) {
doSnackBar("error loading: '" + q + "'");
}
};
the single observables for getVideos and getImages
Single<List<MediaContent>> SingleGetImage(Document document ) {
return Single.create(e -> {
List<MediaContent> result = getImage(document);
if (result != null) {
e.onSuccess(result);
}else {
e.onError(new Exception("No images found"));
}
});
}
Single<List<MediaContent>> singleGetVideo(Document document ) {
return Single.create(e -> {
List<MediaContent> result = getVideo( document);
if (result != null) {
e.onSuccess(result);
}else {
e.onError(new Exception("No videos found"));
}
});
}
assuming you want to execute in parallel the getVideos and getImages requests, you can use flatMap() with zip(), zip will collect the 2 emissions from both Singles, and you can combine the 2 results to a new value, meaning you can sort the videos MediaContent list , and combine it with the images MediaContent list, and return unified list (or whatever other object you'd like):
Single<List<MediaContent>> single =
Single.fromCallable(() -> createDocument(url))
.flatMap(document -> Single.zip(singleGetVideo(document), SingleGetImage(document),
(videoMediaContents, imageMediaContents) -> //here you'll have the 2 results
//you can sort combine etc. and return unified object
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
single.subscribe(parseImageSubscription)
Observable.zip() could implement it perfect. The Observer will receive a merged result by this method.
public void zip() {
Observable<Integer> observable1 = Observable.just(1);
Observable<Integer> observable2 = Observable.just(2);
Observable.zip(observable1, observable2, new Func2<Integer, Integer, Integer>() {
#Override
public Integer call(Integer integer, Integer integer2) {
return integer + integer2;
}
}).subscribe(new Observer<Integer>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Integer o) {
Logger.i(o.toString());
//Here will print 3.
}
});
}
I have some code that first has to run on AndroidSchedulers.mainThread(), then has to do a HTTP request, so has to run on Schedulers.io(), and handle the result on UI, so back to AndroidSchedulers.mainThread().
I receive InterruptedIOException when switching from AndroidSchedulers.mainThread() to Scheulers.io().
Here's some code:
Model model = getModel();
Completable.fromAction(
new Action0() {
public void call() {
mSubject.onNext(model)
}
})
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.andThen(fetchFromServer())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* handle success and error */);
...
public <T> Single<T> fetchFromServer() {
Request request = new Request(); // some request from server, not important
return bodyFrom2(request);
}
public <T> Single<T> bodyFrom2(final Request<T> request) {
return Single.defer(new Callable<Single<T>>() {
#Override
public Single<T> call() throws Exception {
try {
Response<T> response = request.execute();
if (response.error() != null)
return Single.error(response.error().getMessage());
else {
return Single.just(response.body());
}
} catch (IOException e) {
return Single.error(e);
}
}
});
}
public static <T> Single<T> bodyFrom1(final Request<T> request) {
return Single.create(new Single.OnSubscribe<T>() {
#Override
public void call(SingleSubscriber<? super T> subscriber) {
try {
Response<T> response = request.execute();
if (subscriber.isUnsubscribed())
return;
if (response.error() != null)
subscriber.onError(response.error().getMessage());
else {
subscriber.onSuccess(response.body());
}
} catch (IOException e) {
if (subscriber.isUnsubscribed())
return;
subscriber.onError(e);
}
}
});
}
The exception is thrown in bodyFrom() (1 or 2), at request.execute().
I used bodyFrom1(), but I found this question on SO and thought about trying with the second one. Regardless, I receive the exception.
Trying to find what and where the problem is, I tried this:
Completable.complete()
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(Schedulers.io())
.andThen(fetchFromServer())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* handle success and error */);
which still throws InterruptedIOException, and this:
Completable.complete()
.subscribeOn(Schedulers.computation())
.observeOn(Schedulers.io())
.andThen(fetchFromServer())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* handle success and error */);
which works fine.
EDIT:
It seems to work if I'm using Observable or Single instead of Completable.
Added an issue on RxAndroid's Github.
Can I use Retrofit + RxJava to listen to an endless stream? For instance the Twitter stream. What I have is this:
public interface MeetupAPI {
#GET("http://stream.meetup.com/2/rsvps/")
Observable<RSVP> getRSVPs();
}
MeetupAPI api = new Retrofit.Builder()
.baseUrl(MeetupAPI.RSVP_API)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(MeetupAPI.class);
api.getRSVPs()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(rsvp -> Log.d(TAG, "got rsvp"),
error -> Log.d(TAG, "error: " + error),
() -> Log.d(TAG, "onComplete"));
but the "onComplete" is invoked after the first object has been parsed. Is there a way to tell Retrofit to stay open until further notice?
Here my solution:
You can use the #Streaming annotation:
public interface ITwitterAPI {
#GET("/2/rsvps")
#Streaming
Observable<ResponseBody> twitterStream();
}
ITwitterAPI api = new Retrofit.Builder()
.baseUrl("http://stream.meetup.com")
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build().create(ITwitterAPI.class);
With #Streaming we can get raw input From ResponseBody.
Here my function to wrap body divided by lines with events:
public static Observable<String> events(BufferedSource source) {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try {
while (!source.exhausted()) {
subscriber.onNext(source.readUtf8Line());
}
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
subscriber.onError(e);
}
}
});
}
And result usage:
api.twitterStream()
.flatMap(responseBody -> events(responseBody.source()))
.subscribe(System.out::println);
upd about gracefully stopping
When we unsubscribing, retrofit closes inputstream. But impossible to detect inputstream closed or not from inputstream themselves, so only way - try reading from stream - we gets exception with Socket closed message.
We can interpret this exception as closing:
#Override
public void call(Subscriber<? super String> subscriber) {
boolean isCompleted = false;
try {
while (!source.exhausted()) {
subscriber.onNext(source.readUtf8Line());
}
} catch (IOException e) {
if (e.getMessage().equals("Socket closed")) {
isCompleted = true;
subscriber.onCompleted();
} else {
throw new UncheckedIOException(e);
}
}
//if response end we get here
if (!isCompleted) {
subscriber.onCompleted();
}
}
And if connection closed because response end, we haven't any exceptions. Here isCompleted check for that. Let me know if i am wrong :)
Zella's answer is right for Retrofit2 with rxJava,
For rxJava2, I modified custom observable like this:
//imports
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okio.BufferedSource
import java.io.IOException
fun events(source: BufferedSource): Observable<String> {
return Observable.create { emitter ->
var isCompleted = false
try {
while (!source.exhausted()) {
emitter.onNext(source.readUtf8Line()!!)
}
emitter.onComplete()
} catch (e: IOException) {
e.printStackTrace()
if (e.message == "Socket closed") {
isCompleted = true
emitter.onComplete()
} else {
throw IOException(e)
}
}
if (!isCompleted) {
emitter.onComplete()
}
}
}
Changes in module level build.gradle dependencies:
//retrofit rxJava2 adapter
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.7.1'
//rx-java
implementation 'io.reactivex.rxjava2:rxjava:2.2.11'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
Retrofit Adapter Changes:
ITwitterAPI api = new Retrofit.Builder()
.baseUrl("http://stream.meetup.com")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(ITwitterAPI.class);
And Called the Streaming API as
api.twitterStream()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.flatMap { responseBody-> events(responseBody.source()) }
.subscribe({ t ->
Log.i(TAG, "onNext t=$t")
}, { e ->
Log.i(TAG, "onError e=$e")
}, {
Log.i(TAG, "onFinish")
})
I can not understand how to translate a simple AsyncTask in RxJava. Take for example:
private class Sync extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
String proxy_arr = "";
try {
Document jsoup_proxy = Jsoup.connect(Constants.SITE_PROXY_LIST)
.userAgent(Constants.USER_AGENT)
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(Constants.USER_TIMEOUT)
.get();
if (jsoup_proxy != null) proxy_arr = jsoup_proxy.text().trim();
} catch (IOException e) {
new DebugLog(getActivity(), "News", "Sync PROXY", Log.getStackTraceString(e));
}
return proxy_arr;
}
#Override
protected void onPostExecute(String result) {
if (result.equals("err_internet")){
func.toastMessage(R.string.toast_err_nointernet, "", "alert");
}
reloadAdapter();
}
}
As it can be translated in the same working condition RxJava?
Thank you!
Instead of using Observable.create you should use either Observable.defer() or better yet Observable.fromCallable (which was introduced in RxJava 1.0.15) - because these methods will ensure a proper observable contract and save you from some mistakes you can introduce when creating observable by hand.
Also instead of going with runOnUiThread as suggested in one of the answers above, you should really use AndroidSchedulers.mainThread() which was created for exactly this purpose. Just use RxAndroid library which provides it.
I suggest the following solution:
public Observable<String> getJsoupProxy() {
return Observable.fromCallable(() -> {
try {
Document jsoup_proxy = Jsoup.connect(Constants.SITE_PROXY_LIST)
.userAgent(Constants.USER_AGENT)
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(Constants.USER_TIMEOUT)
.get();
return jsoup_proxy != null ? jsoup_proxy.text().trim() : "";
} catch (IOException e) {
// just rethrow as RuntimeException to be caught in subscriber's onError
throw new RuntimeException(e);
}
});
}
getJsoupProxy()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // this scheduler is exported by RxAndroid library
.subscribe(
proxy -> {
if(proxy.equals("err_internet")) {
// toast
}
reloadAdapter();
},
error -> new DebugLog(getActivity(), "News", "Sync PROXY", Log.getStackTraceString(error)));
When you convert a functionality to be reactive please keep in mind that you should define
onNext
onError
onCompleted
"events".
Actually rx is working well with data sequences, but of course you can create a data sequence with only one emitted item.
So to modify your method to be reactive I'd say first you should decouple the responsibilities.
Somewhere in a repository class or you name it according to your architecture you just create this:
public Observable<String> getProxyAsync() {
return Observable.create(subscriber -> {
String proxy_arr = "";
try {
Document jsoup_proxy = Jsoup.connect(Constants.SITE_PROXY_LIST)
.userAgent(Constants.USER_AGENT)
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(Constants.USER_TIMEOUT)
.get();
if (jsoup_proxy != null) proxy_arr = jsoup_proxy.text().trim();
subscriber.onNext(proxy_arr);
} catch (IOException e) {
subscriber.onError(e);
} finally {
subscriber.onCompleted();
}
});
}
after that, somewhere near to the activity, just subscribe to this method like this:
public void myPreciousMethod() {
myCustomRepo.getProxyAsync()
.subscribeOn(Schedulers.newThread())
.subscribe(result -> {
runOnUiThread(() -> {
if (result.equals("err_internet")) {
func.toastMessage(R.string.toast_err_nointernet, "", "alert");
}
});
}, throwable -> {
// some exception happened emmited by your code, handle it well
new DebugLog(getActivity(), "News", "Sync PROXY", Log.getStackTraceString(e));
}, () -> {
// onCompleted:
runOnUiThread(() -> reloadAdapter());
});
}
I would suggest to use .runOnUiThread() (in your activity or any other view related operation with rx) to avoid backpressure, but it really depends on the amount and frequency of emitted data. (you can use .observeOn() and .subscribeOn() as well) Plus using the retrolambda is highly recommended too in sake of much more cleaner code.
This is one way of doing it. You could forego the defer if need be.
Observable.defer(new Func0<Observable<String>>() {
#Override
public Observable<String> call() {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
String proxy_arr = "";
try {
Document jsoup_proxy = Jsoup.connect(Constants.SITE_PROXY_LIST)
.userAgent(Constants.USER_AGENT)
.ignoreContentType(true)
.ignoreHttpErrors(true)
.timeout(Constants.USER_TIMEOUT)
.get();
if (jsoup_proxy != null) proxy_arr = jsoup_proxy.text().trim();
} catch (IOException e) {
new DebugLog(getActivity(), "News", "Sync PROXY", Log.getStackTraceString(e));
}
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(proxy_arr);
}
}
})
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
#Override
public void call(String result) {
if (result.equals("err_internet")){
func.toastMessage(R.string.toast_err_nointernet, "", "alert");
}
reloadAdapter();
}
});