Practice of Perfect Combination of RxJava and Retrofit

Keywords: Retrofit Android network github

Preface

RxJava and Retrofit have also been popular for some time, but recently I have been learning about React Native and Node related postures, and I have not had time to study these new things. Recently, I have a project to write, and I intend to use it first. Android Write a Demo, and find that the Android world has changed dramatically. EventBus and OKHttp are missing. What's the ghost of RxJava and Retrofit?

Well, I went to Github to see RxJava and Retrofit introductions and several Demo s patiently. It makes no sense that Jake Wharton, the God of Android, contributed so much code to the Retrofit project.

If you are not familiar with RxJava, please read it first. RxJava for Android developers This article.

If you are not familiar with Retrofit, read it first. Retrofit official website.

Of course, there are also many articles on RxJava and Retrofit, but I think many of the functions that we all struggle with have not been summarized, so this article has come into being.

Welcome to pat bricks.

Next to enter the text, I think about the combination of RxJava and Retrofit from the following perspectives.

  1. How to Combine RxJava with Retrofit
  2. How to encapsulate Http request data in the same format
  3. Unified preprocessing of Http request data in the same format
  4. How to Cancel an Http Request - Oberver VS Subscriber
  5. What a Subscriber with ProgressDialog should look like

1. How RxJava combines with Retrofit

1.1 Basic Page

Throw out the contents of the build.gradle file first

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.0'
    compile 'io.reactivex:rxjava:1.1.0'
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
    compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
    compile 'com.google.code.gson:gson:2.6.2'
    compile 'com.jakewharton:butterknife:7.0.1'
}

That is to say, this article is based on RxJava 1.1.0 and Retrofit 2.0.0-beta4. Adding rxandroid is due to thread problems in rxjava.

Let's build a basic page. The page is very simple. Let's look at the file directory structure first.

The code for activity_main.xml is as follows:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".activity.MainActivity">

    <Button
        android:id="@+id/click_me_BN"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:padding="5dp"
        android:text="Point me"
        android:textSize="16sp"/>
    <TextView
        android:id="@+id/result_TV"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/click_me_BN"
        android:text="Hello World!"
        android:textSize="16sp"/>
</RelativeLayout>

MainActivity.Java The code is as follows:

package com.queen.rxjavaretrofitdemo.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.TextView;

import com.queen.rxjavaretrofitdemo.R;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.click_me_BN)
    Button clickMeBN;
    @Bind(R.id.result_TV)
    TextView resultTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
    }

    @OnClick(R.id.click_me_BN)
    public void onClick() {
        getMovie();
    }

    //Make network requests
    private void getMovie(){

    }
}

Be careful not to forget to add network privileges

<uses-permission android:name="android.permission.INTERNET"/>

1.2 Retrofit only

We're going to make network requests in the getMovie method. Let's first look at how Retrofit only works.

We use Top250 for Douban Film. test Connection, destination address is

https://api.douban.com/v2/movie/top250?start=0&count=10

As for the data format returned, you can see it when you visit the link yourself. It's too long to put it in.

First, we need to encapsulate an Entity, tentatively named Movie Entity, based on the results returned, so that the code is not pasted.

Next we will create an interface named MovieService, which is coded as follows:

public interface MovieService {
    @GET("top250")
    Call<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

Back in MainActivity, let's write the code for the getMovie method

//Make network requests
private void getMovie(){
    String baseUrl = "https://api.douban.com/v2/movie/";

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    MovieService movieService = retrofit.create(MovieService.class);
    Call<MovieEntity> call = movieService.getTopMovie(0, 10);
    call.enqueue(new Callback<MovieEntity>() {
        @Override
        public void onResponse(Call<MovieEntity> call, Response<MovieEntity> response) {
            resultTV.setText(response.body().toString());
        }

        @Override
        public void onFailure(Call<MovieEntity> call, Throwable t) {
            resultTV.setText(t.getMessage());
        }
    });
}

The above is the code that writes network requests without encapsulated, native Ratofit. We can encapsulate the code that creates the Retrofit and service parts, and then Activity uses the creation of a Callback as a parameter to Call, so that Activity only focuses on the result of the request, and Call has cancel method to cancel a request, as if there is nothing Rxjava, I think I can write this off work.~

The next problem we have to face is that if my Http returns data in a uniform format, for example

{
 "resultCode": 0,
 "resultMessage": "Success",
 "data": {}
}

How do we deal with the returned results in a unified way?

In addition, where should my ProgressDialog show method be called? It looks like it can only be called in getMovie() method. It's annoying to write show() code in the corresponding Listener when you send a request somewhere else.

And I also want to focus on dealing with error requests without posting duplicate code.

Let's first see if things can change with Rxjava. Of course, even without Rxjava, it can still do a lot of encapsulation, but it is more troublesome.

If you need to view the project code - > code address:

https://github.com/tough1985/RxjavaRetrofitDemo

Select Tag - > Step 1

1.3 Add Rxjava

Retrofit itself supports Rxjava.

Adding Retrofit support for Rxjava requires adding it to the Gradle file

compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'

Of course, we have already added it.

Then add the following code in the process of creating Retrofit:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .addConverterFactory(GsonConverterFactory.create())
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .build();

In this way, the service return value we defined is not a Call, but an Observable

Redefining MovieService

public interface MovieService {
    @GET("top250")
    Observable<MovieEntity> getTopMovie(@Query("start") int start, @Query("count") int count);
}

The getMovie method is changed to:

//Make network requests
private void getMovie(){
    String baseUrl = "https://api.douban.com/v2/movie/";

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .build();

    MovieService movieService = retrofit.create(MovieService.class);

    movieService.getTopMovie(0, 10)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<MovieEntity>() {
                @Override
                public void onCompleted() {
                    Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onError(Throwable e) {
                    resultTV.setText(e.getMessage());
                }

                @Override
                public void onNext(MovieEntity movieEntity) {
                    resultTV.setText(movieEntity.toString());
                }
            });
}

This basically completes the combination of Retrofit and Rxjava, but I know you're certainly not satisfied.

Next, we wrap up the process of creating Retrofit, and we want Activity to create Subscriber objects to pass in.

If you need to view the project code - > code address:

https://github.com/tough1985/RxjavaRetrofitDemo

Select Tag - > Step 2

1.4 Encapsulate the request process

Create an object HttpMethods

public class HttpMethods {

    public static final String BASE_URL = "https://api.douban.com/v2/movie/";

    private static final int DEFAULT_TIMEOUT = 5;

    private Retrofit retrofit;
    private MovieService movieService;

    //Construction method private
    private HttpMethods() {
        //Manually create an OkHttpClient and set the timeout
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);

        retrofit = new Retrofit.Builder()
                .client(httpClientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();

        movieService = retrofit.create(MovieService.class);
    }

    //Create a singleton when accessing HttpMethods
    private static class SingletonHolder{
        private static final HttpMethods INSTANCE = new HttpMethods();
    }

    //Get single case
    public static HttpMethods getInstance(){
        return SingletonHolder.INSTANCE;
    }

    /**
     * Data for the Douban Film Top250
     * @param subscriber Observer objects passed from the caller
     * @param start Starting position
     * @param count Get length
     */
    public void getTopMovie(Subscriber<MovieEntity> subscriber, int start, int count){
        movieService.getTopMovie(start, count)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscriber);
    }
}

Encapsulate the object with a singleton and create Retrofit and corresponding Service in the constructor. If you need to access different base addresses, you may need to create multiple Retrofit objects, or simply encapsulate different HttpMethod classes based on different base addresses.

Let's look back at the getMovie method in MainActivity:

private void getMovie(){
     subscriber = new Subscriber<MovieEntity>() {
         @Override
         public void onCompleted() {
             Toast.makeText(MainActivity.this, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
         }

         @Override
         public void onError(Throwable e) {
             resultTV.setText(e.getMessage());
         }

         @Override
         public void onNext(MovieEntity movieEntity) {
             resultTV.setText(movieEntity.toString());
         }
     };
     HttpMethods.getInstance().getTopMovie(subscriber, 0, 10);
}        

subscriber is a member variable of MainActivity.

If you need to view the project code - > code address:

https://github.com/tough1985/RxjavaRetrofitDemo

Select Tag - > Step 3

2. How to encapsulate Http request data in the same format

In the second and third parts, I refer to a question and answer from the knowledgeable. RxJava+Retrofit, how to make a unified judgment after the return of the network? However, there is no complete example, so I will write a complete example here.

In this paragraph, let's talk about some Http services returning data in a fixed format. For example:

{
 "resultCode": 0,
 "resultMessage": "Success",
 "data": {}
}

Most Http services may be set up in this way. The content of resultCode and resultMessage is relatively stable, while the content of data is changeable, 72 changes are not necessarily changeable enough. It may be a User object, an order object, or an order list. According to our previous usage, using Gson transformation requires that we create subscriber objects to specify the return type. If we encapsulate different return values, there may be hundreds of Entities. It is clear that the structure is very clear, but because of the uncertainty of data, we are helpless.

Teenagers, don't bother, come here ~Lao Guo gives you the treasure sunflower, Lao Guo just practices this to become a monk.

We can create an HttpResult class

public class HttpResult<T> {
    private int resultCode;
    private String resultMessage;

    private T data;
}

If data is a User object. Then the return value of the defined Service method can be written as

Observable<HttpResult<User>>

In this way, HttpResult is equivalent to a wrapping class that wraps the results, but gives a clear type when used.

In the example above, I also created an HttpResult class to mimic this form and encapsulate the Subject s separately.

public class HttpResult<T> {

    //Used to mimic resultCode and resultMessage
    private int count;
    private int start;
    private int total;
    private String title;

    //Used to mimic Data
    private T subjects;
}

In this way, generics should be written as follows:

Observable<HttpResult<List<Subject>>>

If you need to view the project code - > code address:

https://github.com/tough1985/RxjavaRetrofitDemo

Select Tag - > Step 4

3. Unified preprocessing of Http request data in the same format

Now that we have the same return format, we may need to do a unified preprocessing after obtaining the data.

When an Http request result is received, the structure returned is unified as follows

{
 "resultCode": 0,
 "resultMessage": "Success",
 "data": {}
}

We want to make a judgment about resultCode and resultMessage first, because if resultCode == 0 stands for success, so resultCode data is usually null at!= 0.

Activity or Fragment have little interest in resultCode or resultMessage, and they are only interested in request status and data data.

Based on this consideration, we throw a custom API Exception when resultCode!= 0. This will go into subscriber's onError, where we can process error messages.

In addition, when the request succeeds, you need to convert the data into the target data type and pass it to subscriber, because Activity and Fragment only want to get the data that is really relevant to them.

This function can be accomplished by using Observable's map method.

Create an internal class HttpResultFunc in HttpMethods with the following code:

/**
 * Result Code for handling Http uniformly and stripping the Data part of Http Result back to subscriber
 *
 * @param <T> Subscriber The data type that you really need, that is, the data type of the Data section
 */
private class HttpResultFunc<T> implements Func1<HttpResult<T>, T>{

    @Override
    public T call(HttpResult<T> httpResult) {
        if (httpResult.getResultCode() != 0) {
            throw new ApiException(httpResult.getResultCode());
        }
        return httpResult.getData();
    }
}

Then our getTopMovie method is changed to:

public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){

    movieService.getTopMovie(start, count)
            .map(new HttpResultFunc<List<Subject>>())
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(subscriber);
}

Since the generic T in HttpResult is the data type we want to pass to subscriber, and the data can be obtained through the getData method of httpResult, we deal with generic problems, error handling problems, and stripping the request data part out to subscriber.

So we just need to focus on the type of Data, not the whole process.

One thing to note is that when defining a Service, generics are

HttpResult<User>
//or
HttpResult<List<Subject>>

When defining Subscriber, the generic type is Java User //or List<Subject>

Otherwise you will get a transition error.

If you need to view the project code - > code address:

https://github.com/tough1985/RxjavaRetrofitDemo

Select Tag - > Step 5

In the code, I simulated the resultCode and resultMessage in HttpResult with bean data, which is slightly different from the code in the document.

4. How to Cancel an Http Request - Observer VS Subscriber

4.1 Cancel an Http request

In this section, let's talk about canceling the Http request. We have Oberver and Subscriber positions, which one of them is easier to give us the G-spot.

If Rxjava is not used, then Service returns a Call, and the Call object has a cancel method to cancel the Http request. So how do you cancel a request after using Rxjava? Because the return value is an Observable. It seems that all we can do is to unsubscribe the Observable object, and nothing else can be done.

Fortunately, Retrofit has helped us to take this into account. The answer can be found in the source code of the RxJavaCallAdapterFactory class.

static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
  private final Call<T> originalCall;

  CallOnSubscribe(Call<T> originalCall) {
    this.originalCall = originalCall;
  }

  @Override public void call(final Subscriber<? super Response<T>> subscriber) {
    // Since Call is a one-shot type, clone it for each new subscriber.
    final Call<T> call = originalCall.clone();

    // Attempt to cancel the call if it is still in-flight on unsubscription.
    subscriber.add(Subscriptions.create(new Action0() {
      @Override public void call() {
        call.cancel();
      }
    }));

    try {
      Response<T> response = call.execute();
      if (!subscriber.isUnsubscribed()) {
        subscriber.onNext(response);
      }
    } catch (Throwable t) {
      Exceptions.throwIfFatal(t);
      if (!subscriber.isUnsubscribed()) {
        subscriber.onError(t);
      }
      return;
    }

    if (!subscriber.isUnsubscribed()) {
      subscriber.onCompleted();
    }
  }
}

We see that in the call method, we add a Subscription object to subscriber. The Subscription object is very simple, mainly to cancel subscriptions. If you look at the source code of Subscriptions.create, you can see that this is the case.

public static Subscription create(final Action0 unsubscribe) {
  return BooleanSubscription.create(unsubscribe);
}

A BooleanSubscription class is used to create a Subscription. If you click in to see the BooleanSubscription.create method, everything is clear. When contacting the binding, subscriber calls the unsubscribe method of Subscription, and then triggers the call method of Action0 passed in when creating Subscription. RxJavaCallAdapterFactory helped us add call.cancel() to subscriber.

To sum up, we create subscriber objects in Activity or Fragment and call unsubscribe method of subscriber when we want to cancel the request.

Sorry, there are too many Subscriber and Subscription, Observer and Observable in this section. I didn't know how many times I vomited when I watched it. I just got used to it.

4.2 Why is Oberver mentioned?

The process of mentioning Observer is like this. Because Subscriber is useless once the unsubscribe method is invoked. And when events are passed to onError or onCompleted, they are automatically unbound. One problem that arises is that every time a request is sent, a new Subscriber object is created.

So we focus on Observer. Observer itself is an interface. Its characteristic is that no matter how you use it, it can't unbind. Why? Because he has no way to unbind. So it's reusable, and I used Observer all the time. In fact, if you use Observer, the Observer object is automatically converted into a Subscriber object when you call the subscribe method of the Observable object.

The following is the source code:

public final Subscription subscribe(final Observer<? super T> observer) {
    if (observer instanceof Subscriber) {
        return subscribe((Subscriber<? super T>)observer);
    }
    return subscribe(new Subscriber<T>() {

        @Override
        public void onCompleted() {
            observer.onCompleted();
        }

        @Override
        public void onError(Throwable e) {
            observer.onError(e);
        }

        @Override
        public void onNext(T t) {
            observer.onNext(t);
        }

    });
}

Later, problems were discovered.

Question 1 cannot be cancelled because Observer does not have unsubscribe method Question 2 does not have onStart method for a chat.

These two problems are painful. So, in order to have a better climax in the future, we still choose Subscriber.

5. What a Subscriber with ProgressDialog should look like

We want a Subscriber to pop up a Progress Dialog every time we send a request, then make the Progress Dialog disappear when the request is accepted, and cancel the current request while we cancel the Progress Dialog, and we just need to process the data inside.

Let's first create a class called ProgressSubscriber to inherit Subscriber.

Subscriber gives us four methods: onStart, onNext, onError, and onCompleted.

Only the onNext method returns the data, so we naturally hope to be able to process the data-related logic in onNext.

The onStart method is used to start a ProgressDialog. In the onError method, we focus on error handling and stop ProgressDialog in the onComplated method.

Among them, we need to solve two problems.

Question 1 onNext Processing Question 2 cancel a request when canceling a Progress Dialog

Let's solve problem 1 first.

5.1 Processing onNext

We hope that we can let Activity or Fragment handle the logic after onNext, and naturally we think of using interfaces. The problem is also a generic one, in which we have to specify a specific type. So interfaces still need generics.

Let's first define an interface named SubscriberOnNextListener

public interface SubscriberOnNextListener<T> {
    void onNext(T t);
}

The code is simple. Take a look at the current code for ProgressSubscriber

public class ProgressSubscriber<T> extends Subscriber<T> {

    private SubscriberOnNextListener mSubscriberOnNextListener;
    private Context context;

    public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) {
        this.mSubscriberOnNextListener = mSubscriberOnNextListener;
        this.context = context;
    }

    @Override
    public void onStart() {
    }

    @Override
    public void onCompleted() {
        Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNext(T t) {
        mSubscriberOnNextListener.onNext(t);
    }
}

I know it's not good to pass Context, but for demonstration, you can encapsulate Toast by yourselves.

MainActivity is used as follows:

First, define a SubscriberOnNextListener object that you can create in onCreate

private SubscriberOnNextListener getTopMovieOnNext;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ButterKnife.bind(this);

    getTopMovieOnNext = new SubscriberOnNextListener<List<Subject>>() {
        @Override
        public void onNext(List<Subject> subjects) {
            resultTV.setText(subjects.toString());
        }
    };
}

The getMovie method writes as follows:

private void getMovie(){
 HttpMethods.getInstance().getTopMovie(
  new ProgressSubscriber(getTopMovieOnNext, MainActivity.this), 
  0, 10);
}

In this way, activities or fragments only need to focus on the logic after getting the results, and the rest need not worry at all.

If you need to view the project code - > code address:

https://github.com/tough1985/RxjavaRetrofitDemo

Select Tag - > Step 6

5.2 Processing ProgressDialog

We hope that when cancel ProgressDialog, we can cancel the subscription and cancel the current Http request. So let's first create an interface to handle this.

public interface ProgressCancelListener {
    void onCancelProgress();
}

Then we use ProgressSubscriber to implement this interface, so ProgressSubscriber has an onCancelProgress method that cancels subscriptions.

@Override
public void onCancelProgress() {
    if (!this.isUnsubscribed()) {
        this.unsubscribe();
    }
}

Then I used a Handler to encapsulate the ProgressDialog.

public class ProgressDialogHandler extends Handler {

    public static final int SHOW_PROGRESS_DIALOG = 1;
    public static final int DISMISS_PROGRESS_DIALOG = 2;

    private ProgressDialog pd;

    private Context context;
    private boolean cancelable;
    private ProgressCancelListener mProgressCancelListener;

    public ProgressDialogHandler(Context context, ProgressCancelListener mProgressCancelListener,
                                 boolean cancelable) {
        super();
        this.context = context;
        this.mProgressCancelListener = mProgressCancelListener;
        this.cancelable = cancelable;
    }

    private void initProgressDialog(){
        if (pd == null) {
            pd = new ProgressDialog(context);

            pd.setCancelable(cancelable);

            if (cancelable) {
                pd.setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialogInterface) {
                        mProgressCancelListener.onCancelProgress();
                    }
                });
            }

            if (!pd.isShowing()) {
                pd.show();
            }
        }
    }

    private void dismissProgressDialog(){
        if (pd != null) {
            pd.dismiss();
            pd = null;
        }
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SHOW_PROGRESS_DIALOG:
                initProgressDialog();
                break;
            case DISMISS_PROGRESS_DIALOG:
                dismissProgressDialog();
                break;
        }
    }
}

Handler receives two messages to control whether to display or close Dialog. When creating Handler, we need to pass in an object instance of ProgressCancelListener.

Finally, post the complete code of ProgressSubscriber:

public class ProgressSubscriber<T> extends Subscriber<T> implements ProgressCancelListener{

    private SubscriberOnNextListener mSubscriberOnNextListener;
    private ProgressDialogHandler mProgressDialogHandler;

    private Context context;

    public ProgressSubscriber(SubscriberOnNextListener mSubscriberOnNextListener, Context context) {
        this.mSubscriberOnNextListener = mSubscriberOnNextListener;
        this.context = context;
        mProgressDialogHandler = new ProgressDialogHandler(context, this, true);
    }

    private void showProgressDialog(){
        if (mProgressDialogHandler != null) {
            mProgressDialogHandler.obtainMessage(ProgressDialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
        }
    }

    private void dismissProgressDialog(){
        if (mProgressDialogHandler != null) {
            mProgressDialogHandler.obtainMessage(ProgressDialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
            mProgressDialogHandler = null;
        }
    }

    @Override
    public void onStart() {
        showProgressDialog();
    }

    @Override
    public void onCompleted() {
        dismissProgressDialog();
        Toast.makeText(context, "Get Top Movie Completed", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onError(Throwable e) {
        dismissProgressDialog();
        Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onNext(T t) {
        mSubscriberOnNextListener.onNext(t);
    }

    @Override
    public void onCancelProgress() {
        if (!this.isUnsubscribed()) {
            this.unsubscribe();
        }
    }
}

So far, the package has been completed. These are some pits that I have trampled in the process of using Rxjava and Retrofit. Finally, they are integrated. Because I have not run through the actual project, if there are any questions, I hope I can bring them up for discussion and welcome them.

Now let's write a new network request. The steps are as follows: 1. Define a new method in Service. 2. Encapsulate the corresponding request in HttpMethods (code can basically copy) 3. Create a Subscriber OnNextListener to process the request data and refresh the UI.

Last

If you find it annoying to write code that changes threads, you can encapsulate the subscription part as well:

public void getTopMovie(Subscriber<List<Subject>> subscriber, int start, int count){
  //The original appearance
// movieService.getTopMovie(start, count)
// .map(new HttpResultFunc<List<Subject>>())
// .subscribeOn(Schedulers.io())
// .unsubscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(subscriber);

 //Modified appearance
    Observable observable = movieService.getTopMovie(start, count)
            .map(new HttpResultFunc<List<Subject>>());

    toSubscribe(observable, subscriber);
}

//Add Thread Management and Subscribe
private void toSubscribe(Observable o, Subscriber s){
     o.subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(s);
}

Let you write as little code as possible and concentrate more on the business logic itself each time you write a request.

Last but not least

If your httpResult format itself is not a problem, but the content of the data is as follows:

{
 "resultCode": 0,
 "resultMessage": "Success",
 "data": {"user": {}, "orderArray": []}
}

Can such a framework continue to be used? My solution is to encapsulate a class with user and orderArray as attributes of the class. But if the data itself is a complete user data for a while on your server, then again, "data": Then I think it's necessary for you to have a good chat with your service provider and invite him to have a meal and a meal of wine. It's no use offering chrysanthemum.

But if the service is already online!!!

Sorry, Sao Nian...

Lao Guo will read Thought in Java 300 times in front of your grave for you~

I hope you can enjoy a new climax with the new positions of Retrofit and Rxjava.

    Posted by balsdorf on Mon, 01 Apr 2019 19:36:30 -0700