In-depth explanation of RxJava (four: using reactive programming in Android)

The original link

is in Articles 1, 2, and 3. I have roughly introduced how RxJava is used. Below I will introduce how to use RxJava in Android.

RxAndroid

RxAndroid is an extension of RxJava for the Android platform. It contains some tools to simplify Android development.

First, AndroidSchedulers provides schedulers for Android's threading system. Need to run some code in the UI thread? Very simple, just use AndroidSchedulers.mainThread():
retrofitService.getImage(url)
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

If you have created your own Handler, you can use HandlerThreadScheduler1 to link a scheduler to your handler.

The next thing to introduce is AndroidObservable, which provides more functions to match the Android life cycle. The bindActivity() and bindFragment() methods use AndroidSchedulers.mainThread() by default to execute the observer code. These two methods will notify the observer to stop sending new messages when the Activity or Fragment ends.
AndroidObservable.bindActivity(this, retrofitService.getImage(url))
    .subscribeOn(Schedulers.io())
    .subscribe(bitmap -> myImageView.setImageBitmap(bitmap);

I myself like the AndroidObservable.fromBroadcast() method, which allows you to create an Observable object similar to a BroadcastReceiver. The following example shows how to be notified of network changes:
IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
    .subscribe(intent -> handleConnectivityChange(intent));

The last thing to introduce is ViewObservable, which can be used to add some bindings to the View. If you want to receive an event every time the view is clicked, you can use ViewObservable.clicks(), or if you want to listen to the content of the TextView, you can use ViewObservable.text().
ViewObservable.clicks(mCardNameEditText, false)
    .subscribe(view -> handleClick(view));

Retrofit

's well-known Retrofit library has built-in support for RxJava. Usually callers can get asynchronous results by using a Callback object:
@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

With RxJava, you can directly return an Observable object.
@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);

Now you are free to use Observable objects as you like. You can not only get data, but also transform.
Retrofit's support for Observables makes it easy to combine multiple REST requests. For example, if we have a request to get photos, and another request to get metadata, we can send these two requests concurrently, and wait for both results to return before processing:
Observable.zip(
    service.getUserPhoto(id),
    service.getPhotoMetadata(id),
    (photo, metadata) -> createPhotoWithData(photo, metadata))
    .subscribe(photoWithData -> showPhoto(photoWithData));

In Part 2 I showed a similar example (using flatMap()). Here I just want to show how simple it is to combine multiple REST requests using RxJava+Retrofit.

Legacy code, extremely slow code

Retrofit can return Observable objects, but what if you're using another library that doesn't support this? Or an internal code that you want to convert into Observables? Is there any easy way?

Most of the time Observable.just() and Observable.from() will help you create Observable objects from legacy code:
private Object oldMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.just(oldMethod());
}

In the above example there is no problem if oldMethod() is fast enough, but what if it is slow? Calling oldMethod() will block its thread.

To get around this, here's what I've been using – wrapping slow code with defer():
private Object slowBlockingMethod() { ... }

public Observable<Object> newMethod() {
    return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}

Now, calls to newMethod() will not block unless you subscribe to the returned observable.

Lifecycle

I saved the hardest part for last. How to handle the life cycle of Activity? There are two main problems:
1. Continue the previous Subscription after configuration changes (such as screen rotation).

Say you make a REST request using Retrofit and then want to display the result in a listview. What if the user rotates the screen at the time of the network request? Of course you want to continue the request just now, but how?

2. Memory leak caused by Observable holding Context

This problem is because when creating a subscription, it holds a reference to the context in some way, especially when you interact with the view, this is too easy to happen! If the Observable does not end in time, the memory footprint will increase.
Unfortunately, there is no silver bullet to fix either of these problems, but here are some guidelines you can follow.

The solution to the first problem is to use RxJava's built-in caching mechanism, so that you can unsubscribe/resubscribe on the same Observable without having to repeatedly run the code to get the Observable. cache() (or replay() ) will continue to perform network requests (even if you call unsubscribe). This means that you can create a new Observable object from the return value of cache() when the Activity is recreated.
Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo));

// ...When the Activity is being recreated...
sub.unsubscribe();

// ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));

Note that both subs are using the same cached request. Of course, where to store the result of the request is up to you, and as with all other lifecycle-related solutions, it must be stored somewhere outside the lifecycle. (retained fragment or singleton, etc.).

The solution to the second problem is to unsubscribe at some point in the lifecycle. A very common pattern is to use CompositeSubscription to hold all Subscriptions, and then cancel all subscriptions in onDestroy() or onDestroyView().
private CompositeSubscription mCompositeSubscription
    = new CompositeSubscription();

private void doSomething() {
    mCompositeSubscription.add(
        AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
        .subscribe(s -> System.out.println(s)));
}

@Override
protected void onDestroy() {
    super.onDestroy ();

    mCompositeSubscription.unsubscribe();
}

You can create a CompositeSubscription object in the base class of Activity/Fragment and use it in subclasses.

Note! Once you call CompositeSubscription.unsubscribe(), the CompositeSubscription object is unavailable, if you want to use CompositeSubscription, you must create a new object.

The solution to both problems requires adding extra code, if anyone has a better solution, please let me know.

To sum up

, RxJava is still a very new project, and RxAndroid is even more. RxAndroid is still under active development, and there are not many good examples. I bet a year from now some of my advice will be seen as outdated.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327100885&siteId=291194637