I'm trying to implement an Observable/Subscriber with RxJava on the onPostExecute() of an AsyncTask and I don't get how to make the connection.
I create the Observable in the onPostExecute method. I want MyFragment to subscribe to this. How do I set this up?
public class LoadAndStoreDataTask extends AsyncTask<String, Integer, String> {
...
#Override
protected void onPostExecute(String result) {
// create the observable
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext(result);
subscriber.onCompleted();
}
}
);
myObservable.subscribe(mySubscriber);
}
}
public class MyFragment extends Fragment {
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Subscriber<String> mySubscriber = new Subscriber<String>() {
#Override
public void onNext(String s) { System.out.println(s); }
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
};
}
...
}
Actually RxJava is supposed to replace AsycTask. In fact I can say with confidence that AsyncTask is a subset of RxJava.
In RxJava, a Subscriber would be analogous to AsyncTask.progressUpdate or onPostExecute and Observable to the process in doInBackground. Data are emitted from Observable to Subscriber and any alteration in this stream is done with mapping methods. You probably don't need mapping now so I would reconfigure my RxJava like this:
Observable<String> myObservable = Observable.create(
new Observable.OnSubscribe<String>() {
#Override
public void call(Subscriber<? super String> subscriber) {
try{
String res = ...//your original doInBackground
subscriber.onNext(res);
// onNext would be comparable to AsyncTask.onProgressUpdate
// and usually applies when backgorund process runs a loop
subscriber.onCompleted();
}catch (SomeException e){
// if the process throws an exception or produces a result
// you'd consider error then use onError
subscriber.onError(e);
}
}
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) // If subscriber runs on UI thread
.subscribe(new Subscriber<String>() {
#Override
public void onNext(String response) {
// result from Observable.onNext. The methods below correspond
// to their Observable counterparts.
}
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
});
AndroidSchedulers is available in RxAndroid. To use it add this line to build.gradle :
compile 'io.reactivex:rxandroid:0.24.0'
Related
I am using RxJava2 and Retrofit2 for handling network requests.
I have cycle where doOnNext should always be ran, but my Activity which is the observer calls dispose() when it is destroyed and that causes retrofit to cancel the request.
java.io.IOException: Canceled
Is there a way to let the request complete but only dispose the UI level observer?
mApi.doSomethingImportant()
.doOnNext(new Consumer<ImportantResponse>() {
#Override
public void accept(ImportantResponse response) throws Exception {
// Store data, should always get here if request is success
}
})
.doOnError(new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
// Store error, should always get here if request fails
}
})
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(AndroidSchedulers.mainThread())
.subscribe(observer); // observer reports success/fail on UI if not disposed
Thanks.
I found one way to achieve the wanted behaviour by wrapping the observer with another, but I'm sure there is some elegant way to do this.
...
.subscribe(new NonDisposableObserver<>(observer)
Where NonDisposableObserver class is following:
public class NonDisposableObserver<T> implements Observer<T> {
private DisposableObserver<T> observer;
public NonDisposableObserver(DisposableObserver<T> observer) {
this.observer = observer;
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(T t) {
if (!observer.isDisposed()) {
observer.onNext(t);
}
}
#Override
public void onError(Throwable e) {
if (!observer.isDisposed()) {
observer.onError(e);
}
}
#Override
public void onComplete() {
if (!observer.isDisposed()) {
observer.onComplete();
}
}
}
I have started learning RxAndroid and below is the code I wrote to iterate over a model object (Results) that contains data fetched from the server. I'm iterating over the model object in the observable and providing a newly created object in the observer. I'm trying to take subscription of the observer to unsubscribe the task upon Orientation changes of the fragment. However the subscribe() returns VOID instead of subscription object.
Questions:
Does the latest version of RxAndroid handle unsubscription itself upon configuration/orientation change?
In case configuration change happens before the task is complete, the only way to restart this task that I can think of is, I persist the server response in onSavedInstance() and retrieve it from bundle when the fragment is recreated. It'll require booleans to figure out if the configuration change happened before the configuration change or not. Is there a graceful and cleaner way of coping with this?
private void createComicList(final List<Result> marvelResults) {
final MarvelComics marvelComics = new MarvelComics();
Observable marvelObservable2 = Observable.create(new ObservableOnSubscribe<MarvelComic>() {
#Override
public void subscribe(ObservableEmitter<MarvelComic> e) throws Exception {
for(Result result : marvelResults) {
MarvelComic marvelComic = new MarvelComic();
marvelComic.setDescription(result.getDescription());
marvelComic.setTitle(result.getTitle());
marvelComic.setPageCount(result.getPageCount());
marvelComic.setThumbnailUrl(result.getThumbnail().getPath());
marvelComic.setId(result.getId());
e.onNext(marvelComic);
}
e.onComplete();
}
});
marvelObservable2.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MarvelComic>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(MarvelComic comic) {
marvelComics.getMarvelComicList().add(comic);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
showToast();
}
});
}
The Observable.subscribe(Observer<? super T>) method returns void in the 2.x since the Observer.onSubscribe(Disposable) is there to get the cancellation support that used to be Subscription in 1.x.
final CompositeDisposable composite = new CompositeDisposable();
Observable<Integer> source = Observable.just(1)
source.subscribe(new Observer<Integer>() {
#Override public void onSubscribe(Disposable d) {
composite.add(d); // <---------------------------------------------
}
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
composite.add(source
.subscribeWith( // <-----------------------------------------------
new DisposableObserver<Integer>() {
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
subscribe() method of Observable returns Subscription object in earlier versions of RxJava and current version returns an object of Disposble class which you can unsubscribe by invoking dispose() method.
For your second question you may check this answer Best practice: AsyncTask during orientation change
I need to download a long list of 30k airports and put it on a offline database.
I made this code to download the json from the web:
bFetch.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(GithubService.SERVICE_ENDPOINT).build();
GithubService service = retrofit.create(GithubService.class);
service.getAirport()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>() {
#Override
public void onCompleted()
{
bClear.setText("OK");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Airport> airports)
{
Log.d("msh",String.valueOf(airports.size()));
}
});
}
});
and it works very well, but if I want to extract only one object, like map or a flatMap, it gives me this:
service.getAirport()
.map(new Func1<List<Airport>, Airport>()
{
#Override
public Airport call(List<Airport> airports) {
return null;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>() {
#Override
public void onCompleted()
{
bClear.setText("OK");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Airport> airports)
{
Log.d("msh",String.valueOf(airports.size()));
}
});
}
});
with the error:
Cannot resolve method 'subscribe(anonymous
rx.Subscriber>)
so:
what I have to do to solve it? My problem is that I don't understand very well rX and I have also a bit confusion
could I put data in realm database in map() method (if it works)?
Thank you
Since you're mapping from a List<Airport> to an Airport, you need to have a Subscriber<Airport> instead of Subscriber<List<Airport>>, along with the same change to the onNext method.
looks like it would compile with Java8 and RxJava2-RC5. I changed subscriber param from List to X and the onNext method from List to X. Maybe you coulde provide some more intel on your environment. Please notice that returning null is not possible anymore in RxJava2.
Furthermore notice that using newThread-Scheduler is not a good idea.
This scheduler simply starts a new thread every time it is requested
via subscri beOn() or observeOn() . newThread() is hardly ever a good
choice, not only because of the latency involved when starting a
thread, but also because this thread is not reused. --Tomasz Nurkiewicz from "Reactive Programming with RxJava"
Example-Impl with RxJava2-RC5
Observable.just(Arrays.asList("1", "2", "3"))
.map(new Function<List<String>, String>() {
#Override
public String apply(List<String> s) throws Exception {
return null;
}
}).subscribeOn(Schedulers.newThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String value) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
I have to use a legacy library that using AsyncTask for a background job. How I can wrap an AsyncTask by an Observable object which I'm using on my current project.
The AsyncTask is encapsulated so I cannot access the synchronous call inside AsyncTask.
say you have an object asynchronousCall executing some async work with call() method which takes callback as a param, you can wrap it like that :
Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(final Subscriber<? super Object> subscriber) {
asynchronousCall.call(new CallBack() {
#Override
public void success(Object o) {
subscriber.onNext(o);
subscriber.onCompleted();
}
#Override
public void error(Throwable t) {
subscriber.onError(t);
}
});
}
});
Another approach to the case when you cannot or don't want to wrap execution in an Observable is to use Subjects:
public static void main(String[] args) {
Subject<Object, Object> subject = PublishSubject.create();
Listener listener = new Listener() {
#Override
public void onCallback(Object object) {
subject.onNext(object);
subject.onCompleted();
}
};
subject.subscribe(object -> yourReaction(object));
someMethodWithCallback(listener);
}
public interface Listener {
void onCallback(Object object);
}
Subject being an Observer allows you to send items into it, while it being an Observable allows you to subscribe to it and receive these events.
I'm looking a way to define order(?) of observers.
#GET("/get_user_msgs")
Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params);
For example I gave a Observable from my Rest API created by Retrofit.
In my ListView I'm observing this Observable.
api.getPrivateMessages(params).subscribe(new Observer());
I also have an API wrapper for my Espresso tests and I'm subscribing to same Observable there. This way observer in API wrapper is called first and only then observer in ListView
is called.
public class IdlingWrapper implements Api, IdlingResource {
....
public IdlingWrapper(Api realApi) {
this.realApi = realApi;
}
...
public Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params); {
counter.incrementAndGet();
return wrapObservable(realApi.getPrivateMessages(params));
}
protected <T> Observable<T> wrapObservable(final Observable<PrivateMessagesResponse> observable) {
//what to do here?
}
}
Is there a way to force some observer to be notified after all others are done? Or something similar in that matter?
Something like
Observable observable = getObservable();
observable.subscribeAsLast(new LastObserver());
observable.subscribe(new ObserverA());
observable.subscribe(new ObserverB());
And so that ObserverA would be notified first, then ObserverB and only then LastObserver.
Or any other approach where I could find out when all registered observers were notified and completed.
I'm not exactly sure what you are trying to do in IdlingWrapper, but I think the current implementation is very fragile.
I think the most important thing that needs to happen is to guarantee the observable can only be called once.
Here is a quick implementation to demonstrate that as well as my implementation of wrapObservable.
public class Test {
private static int counter = 0;
private static final List<Observable<?>> list = Collections.synchronizedList(new ArrayList<>());
protected static <T> Observable<T> wrapObservable(final Observable<T> original) {
// run atleast once???
synchronized (list) {
list.add(original);
}
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
synchronized (list) {
counter++;
if (!list.contains(original)) {
subscriber.onError(new Exception("You can only subscribe once!"));
return;
}
list.remove(original);
}
// Sleep to make it easier to see things happening...
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
subscriber.onCompleted();
}
}).flatMap(new Func1<Void, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Void o) {
return original;
}
}).finallyDo(new Action0() {
#Override
public void call() {
synchronized (list) {
counter--;
if (list.size() == 0 && counter == 0) {
System.err.println("finally");
}
}
}
});
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
// running in io thread for simulating async call.
Observable<String> test = wrapObservable(Observable.from("TEST!!!!!!")).subscribeOn(Schedulers.io());
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
// example of calling the same observable twice.
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
}
Thread.sleep(10000);
}
}
It seems, that this worked just fine.
protected <T> Observable<T> wrapObservable(final Observable<T> original) {
return Observable.create(new Observable.OnSubscribeFunc<T>() {
#Override
public Subscription onSubscribe(final Observer<? super T> t1) {
original.subscribe(new Observer<T>() {
#Override
public void onCompleted() {
t1.onCompleted();
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onError(Throwable e) {
t1.onError(e);
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onNext(T args) {
t1.onNext(args);
}
});
return Subscriptions.empty();
}
});
}
If you want to just use built in RxJava methods to order your observers, you can use flatMap and range to turn each item into multiple items each with a priority and then filter on priority. Observers are ordered based on how they filter.
Here's a trivial example:
Observable<Pair<Integer, Object>> shared = RxView.clicks(findViewById(R.id.textView))
.flatMap(c -> Observable.range(0, 2).map(i -> Pair.create(i, c)))
.share();
shared.filter(p -> p.first == 1)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "first subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "first subscribed onNext"));
shared.filter(p -> p.first == 0)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "second subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "second subscribed onNext"));
If you are doing this all over the place