“This is the third day of my participation in the Gwen Challenge in November. Check out the details: The last Gwen Challenge in 2021.”

🔥 introduction

This paper uses OkHttp+Retrofit+Rxjava+Hilt to implement a network request box.

💥 Final code

        iWanAndroidService.register(map)
                .compose(ResponseTransformer.obtain())
                .subscribe(registerData -> {
                    // The request succeeded
                }, new ErrorConsumer() {
                    @Override
                    protected void error(ApiException e) {
                        // The request failed}});Copy the code

Is it particularly easy?

💥 Project Structure

🔥 OkHttp

What is 💥 OkHttp

OkHttp is a default efficient HTTP client:

  • HTTP/2 support allows all requests to the same host to share a socket.
  • Connection pooling reduces request latency if HTTP/2 is not available.
  • Transparent GZIP reduces download size.
  • Response caching completely eliminates network duplicate requests.
  • After a network problem occurs, OkHttp remains unchanged and automatically recovers from the problem.

The official documentation

Simple use of + source interpretation

I won’t go into OkHttp separately.

Defect:

  • Interface configuration of network request is tedious, especially when complex request body, request header and parameters need to be configured.

  • The data parsing process requires the user to manually get responsBody for parsing, which cannot be reused;

  • Cannot accommodate automatic thread switching.

  • With nested network requests, you fall into the “callback trap.”

🔥 Retrofit

💥 What is Retrofit

Retrofit is based on OkHttp, where network requests are actually done by OkHttp, while Retrofit is primarily responsible for encapsulating interfaces.

Not only does Retrofit have OkHttp’s high performance features, it also has the following advantages:

  • Supports RESTful API design style.

  • Configure requests through annotations: request methods, request parameters, request headers, return values, and so on.

  • Can be used with various Converter to automatically parse and serialize data: Support for Gson, Jackson, Protobuff, etc. Support for RxJava is provided.

  • Request speed is fast, use very convenient and flexible.

Note: Retrofit doesn’t have network requests, so you need to set up dispensers, interceptors, etc., in OkHttpClient.

💥 Retrofit annotations

Official documentation also provides the use of various annotations.

🔥 OkHttp + Retrofit instance

💥 Add a dependency

dependencies {
    implementation 'com. Squareup. Retrofit2: retrofit: 2.8.1' // Necessary dependency, retrofit
    implementation 'com. Squareup. Retrofit2: converter - gson: 2.8.1' // Necessary dependencies to parse JSON characters
    implementation 'com. Squareup. Okhttp3: logging - interceptor: 4.9.0' // No necessary dependency, print logs

}
Copy the code

💥 defines the request interface

public interface IWanAndroidService {
    String BASE_URL = "https://www.wanandroid.com/";
    @GET("banner/json")
    Call<ResponseData<List<HomeBanner>>> homeBanner();

    @POST("user/register")
    @FormUrlEncoded
    Call<ResponseData<RegisterData>> register(@FieldMap Map<String,String> map);

}
Copy the code

💥 set OkHttp + Retrofit

public class NetworkManager {
    private static volatile NetworkManager instances;
    private static volatile OkHttpClient okHttpClient;
    private static volatile Retrofit retrofit;

    public static NetworkManager getInstance(a) {
        if (instances == null) {
            synchronized (NetworkManager.class) {
                if (instances == null) {
                    instances = newNetworkManager(); }}}return instances;
    }

    private static int TIME_OUT = 30; // The connection is disconnected due to a 30-second timeout

    private OkHttpClient initClient(a) {
        if (okHttpClient == null) {
            synchronized (NetworkManager.class) {
                if (okHttpClient == null) {
                    // Request log printing
                    HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(message -> {
                        try {
                            MLog.e(URLDecoder.decode(message, "utf-8"));
                        } catch(UnsupportedEncodingException e) { e.printStackTrace(); MLog.e(message); }}); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);// Note 1: Create OkHttpClient
                    okHttpClient = new OkHttpClient.Builder()
                            .sslSocketFactory(newNetworkSSL(TrustManager.trustAllCert), TrustManager.trustAllCert) .connectTimeout(TIME_OUT, TimeUnit.SECONDS) .addInterceptor(loggingInterceptor) .readTimeout(TIME_OUT, TimeUnit.SECONDS) .writeTimeout(TIME_OUT, TimeUnit.SECONDS) .build(); }}}return okHttpClient;

    }

    public Retrofit initRetrofit(a) {
        if (retrofit == null) {
            synchronized (NetworkManager.class) {
                if (retrofit == null) {
                    // Note 2: Create Retrofit
                    retrofit = new Retrofit.Builder()
                            .client(initClient())/ / optional
                            .baseUrl(IWanAndroidService.BASE_URL)/ / required
                            .addConverterFactory(GsonConverterFactory.create())// Select (data converter, parse).build(); }}}returnretrofit; }}Copy the code
  • Note 1: Create an OkHttpClient object that builds an instance of a network type, typically using the same singleton for all network requests. (If OkHttpClient uses the default, do not set this parameter.)

  • Note 2: Create a Retrofit object, which builds a carrier object for network requests. There is a lot of initialization at build time, such as setting the OkHttpClient, setting the URL of the request, adding data converters, etc.

💥 Network request

            //GET
            // Note 1: Dynamically get the IWanAndroidService object
            IWanAndroidService service = NetworkManager.getInstance().initRetrofit().create(IWanAndroidService.class);
            // Note 2: Network requests
            service.homeBanner().enqueue(new Callback<ResponseData<List<HomeBanner>>>() {
                @Override
                public void onResponse(Call<ResponseData<List<HomeBanner>>> call, Response<ResponseData<List<HomeBanner>>> response) {
                    if(response.body().getData() ! =null) {
                        MLog.e(response.body().getData().get(0).toString());
                        binding.loginTvContent.setText(response.body().getData().get(0).toString()); }}@Override
                public void onFailure(Call<ResponseData<List<HomeBanner>>> call, Throwable t) { MLog.e(t.getMessage()); }});//POST
            Map<String, String> map = new HashMap<>();
            map.put("username", account);
            map.put("password", passsword);
            map.put("repassword", passsword);
            NetworkManager.getInstance().initRetrofit().create(IWanAndroidService.class)
                    .register(map).enqueue(new Callback<ResponseData<RegisterData>>() {
                @Override
                public void onResponse(Call<ResponseData<RegisterData>> call, Response<ResponseData<RegisterData>> response) {
                    if(response.body().getData() ! =null) { MLog.e(response.body().getData().toString()); binding.loginTvContent.setText(response.body().getData().toString()); }}@Override
                public void onFailure(Call<ResponseData<RegisterData>> call, Throwable t) { MLog.e(t.getMessage()); }});Copy the code

The essence of Retrofit: Dynamic proxy setup for unified configuration of network requests.

💥 rendering

🔥 Rxjava

RxJava uses chained calls in the Observer mode and the Builder mode.

Observer mode:

An Observable subscribes to an Observer, notifies the corresponding Observer when it sends a message, and an Observable can Subscribe to multiple observers.

Chain call: call the corresponding method to the original object after processing the original object, so as to do the chain call.

Participants:

  • Observable: The observed, or sender of a message
  • An Observer of the message
  • Subscriber: another representation of an observer
  • Scheduler: a Scheduler that performs thread switching

RxJava is certainly good and powerful, with the following advantages:

  • It has the characteristics of responsive programming.
  • Asynchronous, no need to manually create a thread, and have the ability to switch threads.
  • Support chain call to ensure code simplicity.
  • A variety of operators, very powerful, to meet a variety of business needs.
  • Exception handling is simplified.

RxJava applies to the following scenarios: Network requests, database reads and writes, file reads and writes, and scheduled tasks that need to be performed asynchronously can be performed using RxJava.

💥 Adding a dependency (New)

    implementation "IO. Reactivex. Rxjava2: rxjava: 2.2.6." " // Required RXJava dependencies
    implementation "IO. Reactivex. Rxjava2: rxandroid: 2.1.0." " // The rxandrroID dependency is required for thread cutting. implementation'com. Squareup. Retrofit2: adapter - rxjava2:2.5.0' // Required dependencies, used in conjunction with RxJava
Copy the code

💥 Modify request interface

public interface IWanAndroidService {
    String BASE_URL = "https://www.wanandroid.com/";
    //OkHttp+Retrofit
    //OkHttp+Retrofit+RxJava
    @GET("banner/json")
    Observable<ResponseData<List<HomeBanner>>> homeBanner();

    @POST("user/register")
    @FormUrlEncoded
    Observable<ResponseData<RegisterData>> register(@FieldMap Map<String,String> map);

}
Copy the code

💥 set OkHttp + + RxJava Retrofit

    public Retrofit initRetrofitRxJava(a) {
        if (retrofit == null) {
            synchronized (NetworkManager.class) {
                if (retrofit == null) {
                    retrofit = new Retrofit.Builder()
                            .client(initClient())/ / optional
                            .baseUrl(IWanAndroidService.BASE_URL)/ / required
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())// Add a network request adapter
                            .addConverterFactory(GsonConverterFactory.create())// Select (data converter, parse).build(); }}}return retrofit;
    }
Copy the code

💥 Make a network request

        NetworkManager.getInstance().initRetrofitRxJava()
                .create(IWanAndroidService.class)
                .homeBanner()
                .subscribeOn(Schedulers.io())// Switch to the IO thread
                .observeOn(AndroidSchedulers.mainThread())// Switch to main thread
                // Add a subscription
                .subscribe(listResponseData -> {
                    // The request succeeded
                    if(listResponseData ! =null) {
                        MLog.e(listResponseData.getData().get(0).toString());
                        binding.loginTvContent.setText(listResponseData.getData().get(0).toString());
                    }
                }, throwable -> {
                    // The request failed
                    MLog.e(throwable.getMessage());
                });
Copy the code

💥 rendering

💥 Further encapsulation

Since the request is too cumbersome, let’s try to encapsulate it further.

🌀 Unified exception handling (custom ApiException)

public class ApiException extends Exception {
    // Unknown error
    public static final int UNKNOWN = 1000;
    // Parsing error
    public static final int PARSE_ERROR = 1001;
    // Network error/connection error
    public static final int NETWORK_ERROR = 1002;
    private int code;
    private String displayMessage;

    public ApiException(int code, String displayMessage) {
        this.code = code;
        this.displayMessage = displayMessage;
    }

    public int getCode(a) {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getDisplayMessage(a) {
        return displayMessage;
    }

    public void setDisplayMessage(String displayMessage) {
        this.displayMessage = displayMessage;
    }

    public static ApiException handleException(Throwable e) {
        ApiException ex;
        if (e instanceof JsonParseException
                || e instanceof JSONException
                || e instanceof ParseException) {
            // Parsing error
            ex = new ApiException(PARSE_ERROR, e.getMessage());
            return ex;
        } else if (e instanceof ConnectException) {
            // Network error
            ex = new ApiException(NETWORK_ERROR, e.getMessage());
            return ex;
        } else if (e instanceof UnknownHostException || e instanceof SocketTimeoutException) {
            // Connection error
            ex = new ApiException(NETWORK_ERROR, e.getMessage());
            return ex;
        } else {
            // Unknown error
            ex = new ApiException(UNKNOWN, e.getMessage());
            returnex; }}}Copy the code

🌀 unified exception handling (Consumer implementation<Throwable>Interface)

public abstract class ErrorConsumer implements Consumer<Throwable> {
    @Override
    public void accept(@NotNull Throwable throwable) throws Exception {
        // Handle exceptions
        ApiException exception;
        if (throwable instanceof ApiException) {
            exception = (ApiException) throwable;
        } else {
            exception = ApiException.handleException(throwable);
        }
        // Call the error method
        error(exception);
    }
    // Use the error method.
    protected abstract void error(ApiException e);
}
Copy the code

🌀 Response conversion processing

public class ResponseTransformer<T> implements ObservableTransformer<ResponseData<T>, T> {
    public ResponseTransformer(a) {}public static <R> ResponseTransformer<R> obtain(a){
        return new ResponseTransformer<>();
    }

    @NotNull
    @Override
    public ObservableSource<T> apply(@NotNull Observable<ResponseData<T>> upstream) {
        return upstream.onErrorResumeNext(new Function<Throwable, ObservableSource<? extends ResponseData<T>>>() {
            @Override
            public ObservableSource<? extends ResponseData<T>> apply(@NotNull Throwable throwable) throws Exception {
                return Observable.error(ApiException.handleException(throwable));
            }
        }).flatMap(new Function<ResponseData<T>, ObservableSource<T>>() {
            @Override
            public ObservableSource<T> apply(@NotNull ResponseData<T> responseData) throws Exception {
                // Request successful, start processing your logic
                if (0==responseData.getErrorCode()) {
                    if (null! =responseData.getData()) {return Observable.just(responseData.getData());
                    } else {
                        If Null is returned, an exception will be generated.
                        // Create a data instance with no content with reflection.
                        returnObservable.just(responseData.getData()); }}// The request is abnormal
                return Observable.error(newApiException(responseData.getErrorCode(), responseData.getErrorMsg())); } }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); }}Copy the code

🌀 Is used after encapsulation

Get the data.

🔥 Hilt(Member of JetPack)

Use Hilt for dependency injection on Android. Hilt builds on Dagger and provides a standard way to incorporate Dagger dependency injection into an Android application.

Official documents are the deadliest

💥 Adding a dependency (New)

  implementation 'com. Google. Dagger hilt - android: 2.40.1'
  annotationProcessor 'com. Google. Dagger hilt - compiler: 2.40.1'
Copy the code

💥 Hilt Gradle plugin

🌀 build. Gradle (Project)

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com. Google. Dagger hilt - android - gradle - plugin: 2.40.1'}}Copy the code

🌀 build. Gradle (Module)

apply plugin: 'dagger.hilt.android.plugin'
Copy the code

🌀 Hilt Application

All applications that use Hilt must include an Application class annotated with @hiltAndroidApp.

Create the Application

import android.app.Application;

import dagger.hilt.android.HiltAndroidApp;

@HiltAndroidApp
public class App extends Application {}Copy the code

Set the AndroidManifest


      
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.scc.wanandroid">
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:name=".App"
        .>
    </application>

</manifest>
Copy the code

With all the preparation done, start building the network framework using Hilt

💥 set Retrofit OkHttp + + RxJava + Hilt

🌀 Create NetworkModule for initialization

@InstallIn(SingletonComponent.class)
@Module
public class NetworkModule {
    private static int TIME_OUT = 30; // The connection is disconnected due to a 30-second timeout
    @Provides
    @Singleton
    OkHttpClient providesOkHttpClient(a){
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                try {
                    MLog.e("--network--", URLDecoder.decode(message, "utf-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    MLog.e("--network--", message); }}}); loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);return new OkHttpClient.Builder()
                .sslSocketFactory(new NetworkSSL(TrustManager.trustAllCert), TrustManager.trustAllCert)
                .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                .addInterceptor(loggingInterceptor)
                .readTimeout(TIME_OUT, TimeUnit.SECONDS)
                .writeTimeout(TIME_OUT, TimeUnit.SECONDS)
                .build();
    }
    @Singleton
    @Provides
    Retrofit providesRetrofit(OkHttpClient okHttpClient){
        return newRetrofit.Builder() .client(okHttpClient) .baseUrl(IWanAndroidService.BASE_URL) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();  }@Singleton
    @Provides
    IWanAndroidService providesWanAndroidService(Retrofit retrofit){
        returnretrofit.create(IWanAndroidService.class); }}Copy the code

🌀 use

@AndroidEntryPoint
public class LoginActivity extends AppCompatActivity {
    ActivityLoginBinding binding;
    @Inject
    IWanAndroidService iWanAndroidService;
    //Retrofit+RxJava+Hilt
        iWanAndroidService.homeBanner()
                .compose(ResponseTransformer.obtain())
                .subscribe(homeBanners -> {
                    // The request succeeded
                    if(homeBanners ! =null) {
                        MLog.e(homeBanners.get(0).toString());
                        binding.loginTvContent.setText(homeBanners.get(0).toString()); }},new ErrorConsumer() {
                    @Override
                    protected void error(ApiException e) {
                        // The request failedMLog.e(e.getMessage()+e.getCode()); }}); }Copy the code

🌀 rendering

🔥 summary

The project will work as soon as you get it, and of course there are areas that can be optimized.

  • If the data obtained is null.
  • Hiding the implementation details of network requests from the outside.
  • Further details according to the actual situation, such as API module creation.

💥 Item portal

🔥 Link Summary

🌀 OkHttp Github

🌀 Retrofit Github

🌀 RxJava Github

🌀 Official Hilt documents are the deadliest

🔥 thank

🌀 Thanks for the API provided by WanAndroid

🌀 Android Retrofit + RxJava usage details

🌀 Encapsulate Retrofit2+RxJava2 Network request framework