Android Getting Started tutorial | okhttp + retro use

OkHttp + Retrofit usage example. From introducing dependencies, writing interfaces, to initiating network requests.

Introduce dependency

Introduce dependencies and use Retrofit2.

implementation 'com.squareup.retrofit2:retrofit:2.1.0'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'

query  @ Query

For example, URL   https://base_ URL / backend service / config? Env = dev, after the question mark belongs to the Query content. Whether it is GET or POST, use @ Query annotation. Otherwise, an exception will be reported.

URL filling and splicing

Simple URL filling can be annotated with @ Path. For example, the following post request.

@POST("user-service/user/{uid}/token/refresh")
Call<RefreshTokenResp> refreshToken(@Path("uid") String uid, @Query("token") String token);

GET parameters with query

public interface CfgService {

    @GET("backend-service/config")
    Call<ServerCfgResp> getServerCfg(@Query("env") String env);
}

POST, with query parameters and body

public interface UserService {

    @POST("user-service/login")
    Call<LoginResp> login(@Query("lenovoST") String token, @Query("realm") String realm,
                            @Body RequestBody body);

    @POST("user-service/logout")
    Call<CommonEntity> logout(@Query("token") String token, @Body RequestBody body);
}

Create a RequestBody when calling; First investigate the body types accepted by the background.

Map<String, String> map = new HashMap<>();
        map.put("system", "Android");
        map.put("phoneBrand", Build.BRAND);
        map.put("modelNum", Build.MODEL);
        Gson gson = new Gson();
        String bodyJson = gson.toJson(map);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), bodyJson);

Initialize OkHttpClient; All SSL certificates are trusted here (this is not recommended in formal environments).

private CfgService cfgService;

    public void initService() {
        SSLSocketFactory sslSocketFactory = null;
        try {
            sslSocketFactory = SSLUtils.getSSLSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (sslSocketFactory != null) {
            Log.d(TAG, "sslSocketFactory != null");
            builder.sslSocketFactory(sslSocketFactory);
        } else {
            Log.w(TAG, "sslSocketFactory == null");
        }
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true; // Force return true
            }
        });
        OkHttpClient lenClient = builder.build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(ServerCfg.HOST_URL)
                .client(lenClient)
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        cfgService = retrofit.create(CfgService.class);
    }

Call network request

mNetworkManager.getUserApi().login(mLenovoToken, ServerCfg.RID, requestBody).enqueue(new Callback<LoginResp>() {
            @Override
            public void onResponse(Call<LoginResp> call, final Response<LoginResp> response) {
                //...
            }

            @Override
            public void onFailure(Call<LoginResp> call, final Throwable t) {
                //...
            }
        });

Trust ssl for all servers

This is not recommended

public class SSLUtils {
    /**
 * @return Trust all servers
 */
    public static SSLSocketFactory getSSLSocketFactory() throws Exception {
        SSLSocketFactory sslSocketFactory = null;
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, new TrustManager[]{createTrustAllManager()}, new SecureRandom());
        sslSocketFactory = sslContext.getSocketFactory();
        return sslSocketFactory;
    }

    public static X509TrustManager createTrustAllManager() {
        X509TrustManager tm = null;
        try {
            tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    //do nothing to accept any client certificate
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    //do nothing to accept any server certificate
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
        } catch (Exception e) {

        }
        return tm;
    }

}

service uses io.reactivex.Observable

import io.reactivex.Observable; // This is rx2's bag
// ---

    /**
 * User feedback interface
 *
 * @param content User input feedback
 */
    @POST("feedbackAction")
    Observable<UserFeedback> userFeedback(@Query("appVersion") String appVersion,
                                          @Query("phoneModel") String phoneModel,
                                          @Query("phoneOsVersion") String osVersion,
                                          @Query("submitContent") String content);

Example - Retrofit2, RxJava2

Introduce dependency

implementation 'com.squareup.retrofit2:retrofit:2.5.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.8'

Define interface

import java.util.Map;

import io.reactivex.Observable;
import retrofit2.http.Field;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;

/**
 * RustDrone Background interface
 * Created on 2019-5-17
 */
public interface RustDroneCommonService {

    /**
 * User feedback interface
 *
 * @param content User input feedback
 */
    @FormUrlEncoded
    @POST("feedbackAction")
    Observable<FeedbackResp> userFeedback(@Field("appVersion") String appVersion,
                                          @Field("phoneModel") String phoneModel,
                                          @Field("phoneOsVersion") String osVersion,
                                          @Field("submitContent") String content,
                                          @FieldMap Map<String, String> map);

    /**
 * Get mobile phone verification code
 *
 * @param mobile cell-phone number
 */
    @GET("verifyCode")
    Observable<PhoneCodeResp> getPhoneCode(@Query("mobile") String mobile, @Query("oprType") int type);
}

Call interface

import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

RustDroneDataCenter.getCenter().getCommonService().userFeedback(BuildConfig.VERSION_NAME,
                    Build.MODEL.replace(" ", "-"), Build.VERSION.RELEASE, fd, ext)
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Observer<FeedbackResp>() {
                        @Override
                        public void onSubscribe(Disposable d) {
//                            LL.dn(TAG, "onSubscribe: " + d);
                        }

                        @Override
                        public void onNext(FeedbackResp feedbackResp) {
                            LL.dn(TAG, "onNext: " + feedbackResp);
                            if (feedbackResp.getCode() == 0) {
                                popSubmitSuccessDialog();
                            } else {
                                LL.e("Failed to upload user feedback");
                                mPbLayout.setVisibility(View.GONE);
                                popRetryDialog();
                            }
                        }

                        @Override
                        public void onError(Throwable e) {
                            LL.e("Failed to upload user feedback code: " + e);
                            mPbLayout.setVisibility(View.GONE);
                            popRetryDialog();
                        }

                        @Override
                        public void onComplete() {
                            mPbLayout.setVisibility(View.GONE);
                            LL.dn(TAG, "onComplete: End of upload");
                        }
                    });

OkHttp related interview questions:

1. Briefly describe the advantages of OkHttp

OkHttp is a very excellent network request framework, which has been added to the Android source code by Google. At present, the popular retro fit also uses OkHttp by default.

  • Easy to use and expand.
  • It supports HTTP/2 protocol and allows all requests to the same host to share the same socket connection.
  • If HTTP/2 is not available, use connection pool multiplexing to reduce request latency.
  • GZIP is supported, which reduces the download size.
  • Support cache processing to avoid repeated requests.
  • If your service has multiple IP addresses, OkHttp will try an alternate address when the first connection fails.
  • OkHttp also handles proxy server problems and SSL handshake failures.

2. Talk about the main workflow of okhttp

The first step is to create Request and OkHttpClicent objects, then encapsulate Request into Call objects, and then invoke enqueue() method to execute asynchronous requests.

Step 2: enqueue(AsyncCall) and promoteandexecute () methods of Dispatcher. enqueue(AsyncCall) has two functions: one is to add AsyncCall to readyAsyncCalls in the pre execution queue, and the other is to set the connection counter of the same Host; promoteAndExecute() is responsible for scheduling resources for asynccalls: iterate over readyAsyncCalls. If the size of the queue being executed does not exceed 64 and the value of the connection counter of the same Host does not exceed 5, put the request into runningAsyncCalls. Then traverse runningAsyncCalls and execute the requests one by one;

Step 3: the AsyncCall object submits itself as a task to the thread pool for execution, and finish es after the submission is successful;

3. Interceptor class of okhttp

Official website: interceptor is a powerful mechanism provided in Okhttp. It can realize network monitoring, request, response rewriting, request failure retry and other functions.

  • RetryAndFollowUpInterceptor: retry and failure redirection interceptor
  • BridgeInterceptor: a bridge interceptor that handles some necessary request header information
  • CacheInterceptor: a cache interceptor that handles caching
  • ConnectInterceptor: connecting interceptors and establishing available connections are the basic functions of CallServer interceptor
  • CallServerInterceptor: requests the server interceptor to write the http request into the IO stream and read the Response from the IO stream

4. Please briefly describe how to use OKHttp to initiate network requests?

The steps to initiate a network request using OKHttp are as follows:

  • New OKHttpClient
  • Create a new Request object based on the requested URL
    The parameters used in the Request process are placed in the RequestBody of the Request object
  • Create a new Call object using the Request object
  • The synchronous request executes call.execute(), and the asynchronous request executes call.enqueue
  • Get the request execution result object Response and parse it

5. How to use OKHttp to process cookies?

There are two ways to process cookies using OKHttp:

The first is manual processing. In the response object of the callback, all header information, including Cookie information, can be obtained through response.headers(). At this time, we can save the Cookie locally and add the Cookie information to the header the next time we initiate a network request.

The second is to use the cookie jar provided by OKHttp. The specific code is as follows:

builder = new OkHttpClient.Builder();  
builder.cookieJar(new CookieJar() {  
    private final HashMap<String, List<Cookie>> cookieStore = new HashMap<String, List<Cookie>>();  
  
    @Override  
    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {  
        cookieStore.put(url.host(), cookies);  
    }  
  
    @Override  
    public List<Cookie> loadForRequest(HttpUrl url) {  
        List<Cookie> cookies = cookieStore.get(url.host());  
        return cookies != null ? cookies : new ArrayList<Cookie>();  
    }  
});  
okHttpClient = builder.build();  

Among them, saveFromResponse() will be executed when receiving the server response. At this time, the Cookie information can be saved, and loadForRequest() will be executed when initiating the network request. This is to add the saved Cookie information.

6. What optimizations does okhttp have for network requests and how to implement them?

Compared with the native HttpUrlConnection, OKHttp is optimized as follows:

  • Support HTTP2/SPDY
  • socket automatically selects the best route and supports automatic reconnection
  • It has an automatically maintained socket connection pool to reduce the number of handshakes and response delay
  • With queue thread pool, easy write concurrency
  • Have Interceptors to easily handle requests and responses (such as transparent GZIP compression, LOGGING)
  • Cache strategy based on Headers to avoid repeated network requests

[Android Development: Framework source code analysis video reference]

Tags: Android

Posted on Tue, 23 Nov 2021 03:33:50 -0500 by g7pwx