I’ll put the ## in front

The previous two articles covered almost all of the basics, and if you can get through the first two, RxJava is a complete primer. After that, you have to learn some more advanced usage and principles.

So this article introduces another core content in RxJava – transformation. Originally prepared to connect its principle together to explain, but the transformation of the principle of a bit more complex, if linked together to write may be too long, it looks more boring. So let’s not talk about the source code for the moment, but take a look at its basic use. Of course, it is still explained in the form of small cases, so that it will not be boring.

FuncX and ActionX in RxJava are FuncX and ActionX in RxJava, and they are FuncX and ActionX in RxJava.

So the table of contents is as follows:

  • Writing in the front
  • Magic transformation
    • review
    • Map
    • FuncX and ActionX
    • FlatMap
  • The Scheduler then words
  • conclusion
    • The resources
    • Program source code

## Magical transformation

# # # to review

Before we talk about transformation operations, let’s review the small case of asynchronously fetching network images.

Create (new Observable.OnSubscribe<Bitmap>() {/** * overwrite the call method ** @param subscriber observer */ @Override public void call(Subscriber<? Super Bitmap > the subscriber) {/ / through the URL to get the image Bitmap object Bitmap Bitmap. = GetBitmapForURL getBitmap (URL); // Call back the observer method subscriber.onnext (bitmap); subscriber.onCompleted(); }}). SubscribeOn (Schedulers. IO ()) / / specify the subscribe () in IO thread. ObserveOn (AndroidSchedulers. MainThread ()) / / Subscribe (new Observer<Bitmap>() {// Subscribe (new Observer<Bitmap>) @override public void onNext(Bitmap) bitmap) { mainImageView.setImageBitmap(bitmap); } @Override public voidonCompleted() {
    	mainProgressBar.setVisibility(View.GONE);
        Log.i(" onCompleted ---> "."Complete");
    }

    @Override
    public void onError(Throwable e) {
    	Log.e(" onError --->", e.toString()); }});Copy the code

Observable Create specifies that the type of the object to be sent is a Bitmap. This Bitmap is obtained from the Url of the image. Then send Subscriber to the Observer (that is, Observer. I would like to emphasize again that Subscriber should be used to replace Observer in the following articles, for the reason that has been emphasized in the previous article, so I will not repeat it again).

In short, we specify the type of object to send from the beginning.

Is it possible to imagine that we send a String Url and then somehow send the resulting Bitmap?

Here’s what it looks like:

###Map

Of course there is. RxJava provides an operator called Map, whose official definition looks like this:

The Map operator applies a function of your choice to each data emitted by the original Observable, and returns an Observable that emits those results.

This simply means that it applies a function to every data that an Observable sends and performs transformations in the function.

If you still don’t understand, draw a picture to make sense of it. This picture is also the official one, but I redrew it:

As you can see from the figure, this is a one-to-one conversion, that is, one single data is converted to another single data, which needs to be compared with the flatmap, so pay attention to this sentence.

Now that we know the basics, let’s make a change to the previous code:

Observable. Just (Url).map(new Func1<String, Bitmap>() {@override public Bitmap call(String s) {// Send the call by converting the Map to a BitmapreturnGetBitmapForURL.getBitmap(s); }}). SubscribeOn (Schedulers. IO ()) / / specify the subscribe () in IO thread. ObserveOn (AndroidSchedulers. MainThread ()) / / The callback to specify Subscriber occurs in the UI thread // as you can see, the type accepted here is Bitmap, Subscribe (new Action1<Bitmap>() {@override public void call(Bitmap Bitmap) { mainImageView.setImageBitmap(bitmap); mainProgressBar.setVisibility(View.GONE); }});Copy the code

Here we pass in a String Url using the Just operator, then return a Bitmap using the Func1 call method in the map operator, and finally receive a Bitmap in the SUBSCRIBE Action1 class.

This successfully converts the type sent by the original Observable into another type via map. This is where the map operator comes in handy.

# # # FuncX ActionX

In the above code, these two classes appear: Func1 and Action1. What does that mean?

####ActionX

Let’s explain Action1 first. Click on the source code:

/**
 * A one-argument action.
 * @param <T> the first argument type
 */
public interface Action1<T> extends Action {
    void call(T t);
}
Copy the code

An interface with a single argument and a call method with no return value. Since onNext(T obj) and onError(Throwable error) are also single arguments with no return value, Action1 can therefore wrap onNext(obj) and onError(error) and pass subscribe() to implement an incompletely defined callback.

That is, this callback only calls onNext and onError, not the whole callback.

For such incomplete callbacks, RxJava automatically creates Subscriber based on the definition.

In addition, similar to Action1 is Action0, which is also more commonly used, still click in to see the source code:

/**
 * A zero-argument action.
 */
public interface Action0 extends Action {
    void call();
}
Copy the code

Since the onCompleted() method also takes no parameters and returns no value, Action0 can be used as a wrapper object to wrap up the contents of onCompleted(). Subscribe () is passed as an argument to implement an incompletely defined callback.

In addition:

RxJava provides multiple ActionX-like interfaces (e.g. Action2, Action3) that can be used to wrap different methods with no return value.

Okay, let’s put it in more general terms:

  • Action0 simply passes subscribe() as argument onCompleted().
  • Action1 simply passes subscribe() with onNext() and onError() as arguments.

####FuncX

Now that you know ActionX, look at Func1. Click on the source code:

/**
 * Represents a function with one argument.
 * @param <T> the first argument type
 * @param <R> the result type
 */
public interface Func1<T, R> extends Function {
    R call(T t);
}
Copy the code

In contrast, it is easy to see that it is similar to Action1 and is also an INTERFACE to RxJava. One notable difference, however, is that Func1 wraps methods that return values.

And FuncX, like ActionX, has a number of methods, mainly for different numbers of parameters. Let’s just remember one thing:

The difference between FuncX and ActionX is that FuncX wraps a method that returns a value.

###FlatMap

Ok, so that’s Map. Remember we emphasized earlier that Map is a one-to-one conversion, but is there a one-to-many conversion? Of course, FlatMap.

Let’s look at the official definition:

The FlatMap operator transforms each data emitted by the original Observables using a specified function that returns an Observable that also emits data. The FlatMap then merges the data emitted by the Observables. Finally, the combined result is transmitted as its own data sequence.

Well, the definition is always confusing. Never mind, now try to illustrate it in graphic form:

In simple terms, each data in a set of data is transformed, and then the transformed data is combined into a sequence for transmission.

However, it should be noted that each transformed data is also an Observable that can send data, so the figure above can be simplified as follows:

As can be seen from the diagram, FlatMap can perform one-to-many conversion compared with Map.

Well, without further ado, let’s look at specific cases. The example is a GridView that asynchronously loads multiple network images.

I want to make two points about this project:

  • How to use GridView
  • This project is not the best example for introducing FlatMap features, but I’ll use it for the moment for comparison purposes.

Ok, to review our previous use of the FROM operator, we add the urls of four images to a single set of Url data:

private final String url1 = "Http://www.iamxiarui.com/wp-content/uploads/2016/06/. PNG";
private final String url2 = "Http://www.iamxiarui.com/wp-content/uploads/2016/06/ why my traffic again. PNG";
private final String url3 = "http://www.iamxiarui.com/wp-content/uploads/2016/05/cropped-iamxiarui.com_2016-05-05_14-42-31.jpg";
private final String url4 = "Http://www.iamxiarui.com/wp-content/uploads/2016/05/ WeChat PNG".; Private final String[] urls = new String[]{urL1, URl2, url3, urL4};Copy the code

Then let’s see how flatMap works:

// Pass the String Url Observable.from(urls). FlatMap (new Func1<String, Observable<String>>() { @Override public Observable<String> call(String s) {returnObservable.just(s); }})Copy the code

As you can see, it converts a set of String Urls into an Observable that sends a single String Url.

Now that it’s converted to an Observable that sends individual data, it’s much easier to use the map operator you just learned:

.map(new Func1<String, Bitmap>() {@override public Bitmap call(String s)returnGetBitmapForURL.getBitmap(s); }}). SubscribeOn (Schedulers. IO ()) / / specify the subscribe () in IO thread. ObserveOn (AndroidSchedulers. MainThread ()) / / The callback to specify Subscriber occurs in the UI thread // as you can see, the type accepted here is Bitmap, Subscribe (new Action1<Bitmap>() {@override public void call(Bitmap Bitmap) { mainImageView.setImageBitmap(bitmap); mainProgressBar.setVisibility(View.GONE); }});Copy the code

Now let’s look at the runtime dynamic diagram:

As you can see, it loads each image in turn.

Remember when I said this wasn’t the best case, why? Because Flatmap has one feature:

The FlatMap merging of the data emitted by these Observables may be staggered.

What does that mean? That is, this set of data may change order when converted into individual data, and in my case, that didn’t happen, so I say it’s not a perfect case.

So one asks, how do you keep it from interleaving?

RxJava also provides us with a concatMap operator, which is similar to the simplest version of flatMap, but it concatenates rather than merges the generated Observables in order, and then generates its own data sequence.

This is a little bit easier, so I’m not going to write a case study.

Ok, so that’s the end of the common and very important transformation operators. The following articles will analyze how this works.

# # to the Scheduler

Finally, there are some additions to Scheduler.

Remember the two operators that Scheduler introduced earlier:

  • SubscribeOn (): Specifies the thread in which the subscribe() subscription occurs, that is, the thread in which the call() is executed. Or the thread of event generation.
  • ObserveOn (): Specifies the thread on which the Observer runs, that is, the thread on which onNext() executes. Otherwise known as event-consuming threads.

Now let’s introduce two more operators.

###doOnSubscribe

Previously, when saying the difference between Subscriber and Observer, we mentioned that there are two more methods of Subscriber. The onStart() method occurs after the SUBSCRIBE () method is called and is an initialization method before the event is sent. But this initialization does not specify a thread.

In my case, there is a progress bar, and if you want to display the progress bar you have to execute it in the main thread. But we don’t know in advance what threads are specified by the subscribeOn() method. So it is risky to perform some initialization in the onStart method.

So what to do?

RxJava provides us with another operator, doOnSubscribe, which, like onStart, is executed after the subscribe() method is called and before the event is sent, so we can initialize in this operator as well. The difference is that it can specify threads.

By default, doOnSubscribe() is executed on the thread where subscribe() occurs; If doOnSubscribe() is followed by subscribeOn(), it will execute the thread specified by the closest subscribeOn() to it.

I have two questions about this statement:

  • Does the default thread execute on which subscribe() occurs?
  • What is the thread specified by the nearest subscribeOn()?

Questions aside, let’s look at usage:

Map (new Func1<String, Bitmap>() {@override public Bitmap call(String s) {log.i ()" map ---> "."Performing");
			Log.i(" map ---> ", Thread.currentThread().getName());
			returnGetBitmapForURL.getBitmap(s); }}).subscribe (schedulers.io ()) // Subscribe() occurs on the IO threadAction0() {@override public void is required in the main threadcall() {
			mainProgressBar.setVisibility(View.VISIBLE);
			Log.i(" doOnSubscribe ---> "."Performing");
			Log.i(" doOnSubscribe ---> ", Thread.currentThread().getName()); }}). SubscribeOn (AndroidSchedulers mainThread ()) / / specify the subscribe () in the main thread. ObserveOn (AndroidSchedulers. MainThread ()) / / Subscribe (new Action1<Bitmap>() {@override public void call(Bitmap Bitmap) {Override public void call(Bitmap Bitmap) { mainImageView.setImageBitmap(bitmap); mainProgressBar.setVisibility(View.GONE); Log.i(" subscribe ---> "."Performing");
			Log.i(" subscribe ---> ", Thread.currentThread().getName()); }});Copy the code

Here is the execution Log:

As you can see, when triggered from onClick(), doOnSubscribe() is executed, then map() is executed, and finally subscribe() is executed. That is, it is actually called before the data is sent, and is fully capable of initialization.

Ok, now let’s settle the question. Let’s settle point two: what’s the most recent? Change the code to this:

. .subscribe (schedulers.newthread ()) // subscribeOn(schedulers.newthread ()) // subscribe() occurs on the new threadAction0() {@override public void is required in the main threadcall() {
		Log.i(" doOnSubscribe ---> "."Performing");
		Log.i(" doOnSubscribe ---> ", Thread.currentThread().getName()); }}). SubscribeOn (Schedulers. IO ()) / / specify the subscribe () in IO thread. SubscribeOn (AndroidSchedulers. MainThread ()) / / Specify subscribe() occurs on the main thread...Copy the code

I intentionally wrote doOnSubscribe between two subscribeOn subscribeOn subscribeOn, and now look at the log:

It is obvious from the log that doOnSubscribe() is executed on the IO thread, so the conclusion is:

  • If subscribeOn() is specified after doOnSubscribe(), it determines in which thread doOnSubscribe() is executed.
    • (1) The subscribeOn() before doOnSubscribe() does not affect it.
    • (2) subscribeOn() after doOnSubscribe(), and it will only be affected if it is the most recent.

Which brings us to the second question: Where is the default thread? Change the code to this:

. .subscribe (schedulers.io ()) // subscribeOn(schedulers.io ()) // subscribe() occurs on the IO threadAction0() {@override public void is required in the main threadcall() {
		Log.i(" doOnSubscribe ---> "."Performing");
		Log.i(" doOnSubscribe ---> ", Thread.currentThread().getName()); }}).observeon (schedulers.io ()) // The Subscriber callback occurs on the IO thread...Copy the code

Look at the Log:

If you look at this log, you’re going to be wondering, and I was wondering, why do you subscribe to IO threads and observeOn() and doOnSubscribe() and then not subscribe to BEon ()? By default, it should execute on the subscribe() thread.

Subscribe () is already assigned to the IO thread by observeOn(), so it should execute on the IO thread.

I looked through the WiKi, looked for a lot of materials, and even looked at the source code, but could not find the reason.

If anyone knows, please let me know, thanks!

###doOnNext

Since the FROM and FlatMap operators can send multiple data, what if we wanted to prompt each data delivery to tell us that another data was sent?

RxJava provides us with the doOnNext() operator, which allows us to do other things, such as prompt and save, each time we output an element.

This code is the complete code for the flatMap example above, as shown below:

Observable.from(urls)
	.flatMap(new Func1<String, Observable<String>>() {
		@Override
		public Observable<String> call(String s) {
			return Observable.just(s);
		}
    })
	.map(new Func1<String, Bitmap>() {
		@Override
		public Bitmap call(String s) {
			returnGetBitmapForURL.getBitmap(s); }}). SubscribeOn (Schedulers. IO ()) / / specify the subscribe () in IO thread. ObserveOn (AndroidSchedulers. MainThread ()) / / DoOnNext (new Action1<Bitmap>() {Override public void call(Bitmap Bitmap) { Toast.makeText(OtherActivity.this,"Add pictures", Toast.LENGTH_SHORT).show(); Subscribe (new Action1<Bitmap>() {@override public void call(Bitmap Bitmap) {// Add the Bitmap to the set list.add(bitmap); // Set the image gvother. setAdapter(new GridViewAdapter(otherActivity.this, list)); pbOther.setVisibility(View.GONE); }}); }Copy the code

Take a look at the runtime dynamic diagram:

As you can see, each image is loaded with a popover indicating that the image is added, which is what the doOnNext operator does.

# # epilogue

Ok, that’s all for today. Most of it is usage, which is more or less complicated than basic usage, so I call it intermediate usage.

The same as the basics above, the use of the end of the need to understand its principle. So the following articles will explain the principle of transformation, still through the form of text and text easily to learn.

And every time in the process of writing an article, I can find my improper understanding or mistakes in the learning process, and now share them. But there must be something wrong, so I hope you can correct me or communicate with me if you have different opinions, thank you!

RxJava: a first understanding of RxJava and basic application

RxJava: two, easy to learn the basis of the source code

### Resources

RxJava for Android developers

RxJava documentation and tutorials

RxJava(2: Operator)

### project source code

IamXiaRui-Github-FirstRxJavaDemo


Personal blog: www.iamxiarui.com

Original link: www.iamxiarui.com/?p=773