preface
This article focuses on the use of Rxjava in conjunction with Retrofit. For those unfamiliar with Rxjava and Retrofit, check out my previous two introductions
- Android RxJava: Basic introduction and use
- Android RxJava series 2: Common extension operators
- Android Retrofit 2.5.0 uses basic details
The basic use
Define the request interface
public interface GetRequest_Interface {
@POST("/app/auth/school/list") Observable<School> postSchool(@Body RequestBody route); // Get school by school name}Copy the code
Create a Retrofit interface instance
GetRequest_Interface request = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(GetRequest_Interface.class);
Copy the code
Build request parameters
The request body is a Json string
HashMap<String, Object> map = new HashMap<>();
map.put(key, value);
Gson gson=new Gson();
String strEntity = gson.toJson(map);
KLog.d("22222222RequestBody"+strEntity);
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=UTF-8"),strEntity);
Copy the code
Start network request
Observable<School> observable = request.postSchool(body); observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<School>() { Override public void onSubscribe(Disposable d) {Override public void onSubscribe(Disposable d) {Override public void onSubscribe(Disposable d) School) {Override public void onError(Throwable e) {Override public void onError(Throwable e) { = 200 ) } @Override public voidonComplete() {// request completes processing, closes request queue, closes dialog, etc.}});Copy the code
At this point,Rxjava and Retrofit combined with the basic use of the end, based on the above can be happy to complete the network request work, is not very convenient and concise.
Of course, for our business, it is impossible to have only one network request, basically everywhere need to go into the network request, and it is not as simple as above. There are other parameters that need to be configured in our business. If every network request is written like this, of course it is possible, but your code is too low and does not conform to programming specifications.
So based on the premise that you will be familiar with the use of the above code, we need to carry out a simple package.
I want to say one more thing about encapsulation
A lot of people think of encapsulation as making code simple enough, logic clear enough, open and closed, and so on, and that’s true, but it depends, and if you’re writing code for a broad audience, which is an open source project, there are a lot of things to be open about.
But if we only use it in our own projects, we must make it clear that the premise of customization is to meet the needs of our business, not too encapsulated. This is why we often need to re-package other people’s open source frameworks. There is no best package, only the most appropriate package.
Packaging paper
Unified Url storage
Public interface API {// Store all BaseUrl String BASE_URL ="http://xx.xxx.xx.225:8080"; // Core API String BASE_SCHOOL_URL ="http://xx.xxx.xx.225:8081"; // school API}Copy the code
Store all request apis
Public interface GetRequest_Interface {/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- all network request API-------------------------------------------------------*/ @POST("/app/auth/captcha") Observable<Phone> postPhone(@Body RequestBody route); // Get the verification code @post ("/app/auth/login") Observable<RegistLogin> postRegist(@Body RequestBody route); // Register}Copy the code
Encapsulates the parameter information needed to process a network request
- Initialize the OkHttpClient configuration
OkHttpClient client = new okHttpClient.builder ().connectTimeout(DEFAULT_TIME_OUT, Timeunit.seconds) //l connection timeout duration. WriteTimeout (DEFAULT_READ_TIME_OUT, timeunit.seconds) // read/writeTimeout ReadTimeout (DEFAULT_READ_TIME_OUT, TimeUnit. SECONDS) / / read timeout retryOnConnectionFailure (trueAddNetworkInterceptor (tokenRespInterceptor) addNetworkInterceptor(tokenRespInterceptor) //.authenticator(authenticator) // Authorize authentication.build ();Copy the code
OkHttp3 interceptors are used here. If you are not familiar with them, you may want to know about them
- Add a common request header uniformly
Interceptor tokenInterceptor = new Interceptor() {// Global interceptor, @Override public okhttp3.Response intercept(Chain chain) throws IOException { Request originalRequest = chain.request(); / / get Request. The original Request Builder requestBuilder = originalRequest. NewBuilder () / / a new Request. AddHeader ("Accept"."application/json")
.addHeader("Content-Type"."application/json; charset=utf-8")
.removeHeader("User-Agent")
.addHeader("User-Agent",BaseUtils.getUserAgent())
.method(originalRequest.method(),originalRequest.body());
returnchain.proceed(requestBuilder.build()); // request againCopy the code
- The Token is dynamically added globally
Interceptor tokenInterceptor = new Interceptor() {// Global interceptor, add token field to request header, Override public okHttp3.Response Intercept (Chain Chain) throws IOException {Request originalRequest = chain.request(); / / get Request. The original Request Builder requestBuilder = originalRequest. NewBuilder () / / a new Request. AddHeader ("Accept"."application/json")
.addHeader("Content-Type"."application/json; charset=utf-8")
.removeHeader("User-Agent")
.addHeader("User-Agent",BaseUtils.getUserAgent())
.method(originalRequest.method(),originalRequest.body());
// Log.e("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -",originalRequest.body().toString()); Request tokenRequest = null; // Request with tokenif (StringUtils.isEmpty(App.mmkv.decodeString(BaseConfig.TOKEN,null))){
return chain.proceed(requestBuilder.build());
}
tokenRequest = requestBuilder
.header("Authorization"."Bearer "+App.mmkv.decodeString(BaseConfig.TOKEN,null))
.build();
returnchain.proceed(tokenRequest); }};Copy the code
Here, Tencent’s MMKV framework is used to store the tokens locally. Since we did not get the tokens at the beginning, we need to add them dynamically
- The token automatically checks whether the token is expired
In network interaction, the Token issued by the server is time-limited and generally short. After the expiration of the validity period, a request for a new Token needs to be made. This process cannot be perceived by users, so users need to refresh and request for a new Token without being aware of it.
Interceptor tokenRespInterceptor = new Interceptor() {// Intercept the return body to determine whether the token is expired. Override Public Response Intercept (Chain Chain) throws IOException {Request Request = chain.request(); Response response = chain.proceed(request); // KLog.d( response.body().string());if (isTokenExpired(response)){
KLog.d( "Automatically refresh Token and then request data again"); String newToken = getNewToken();if(newToken ! Request newRequest = chain.request().newBuilder().header()"Authorization"."Bearer "+ newToken) .build(); // request againreturnchain.proceed(newRequest); }}return response.newBuilder().body(ResponseBody.create(MediaType.parse("UTF-8"),body)).build(); }};Copy the code
Here needs to be judged according to the expiration rules agreed by the server, here is a simple demonstration
/** * Check whether Token is invalid ** @param Response * @return
*/
private boolean isTokenExpired(Response response) {
try {
body = response.body().string();
JSONObject object = new JSONObject(body);
message = object.getString("Message");
code = object.getInt("Code");
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
if ("Token is expired".equals(message)&& code == 1) {
return true;
}
return false;
}
Copy the code
Get a new Token
/** * Synchronizes the request to obtain the latest Token ** @return
*/
private String getNewToken() {// Through the token interface, GetRequest_Interface Request = new retrofit.builder ().baseurl (api.base_url) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build().create(GetRequest_Interface.class); // KLog.e(Remember.getString("refresh_token",null));
RequestBody body = BaseUtils.convertJson(BaseUtils.paramsMap("refresh_token",App.mmkv.decodeString(BaseConfig.REFRESH_TOKEN,null)));
Call<RefreshToken> call = request.postRefreshToken(body);
try {
response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
KLog.e(response.body().getCode()+response.body().getMessage());
if (response.code() == 200 &&response.body().getCode() ==0){
newToken = response.body().getData().getToken();
KLog.e("-- -- -- -- -- -- -- -- -- -- -- -- -- -- --"+newToken);
App.mmkv.encode(BaseConfig.TOKEN,newToken);
App.mmkv.encode(BaseConfig.SCHOOL_TOKEN,response.body().getData().getSchool_token());
App.mmkv.encode(BaseConfig.EXPIRE_IN,response.body().getData().getExpire_in());
}
return newToken;
}
Copy the code
- Initialize Retrofit
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
Copy the code
At this point, the parameters related to the network request are basically configured
Encapsulate the above configuration steps
/** * Created by Darryrzhong ** * unified Retrofit portal */ public class RetrofitHelper {private static final int DEFAULT_TIME_OUT = 5; Private static final int DEFAULT_READ_TIME_OUT = 10; private static final int DEFAULT_READ_TIME_OUT = 10; private final Retrofit retrofit; private String body; private retrofit2.Response<RefreshToken> response; private String newToken; private String message; private int code; privateRetrofitHelper(){
Interceptor tokenInterceptor = new Interceptor() {// Global interceptor, add token field to request header, Override public okHttp3.Response Intercept (Chain Chain) throws IOException {Request originalRequest = chain.request(); / / get Request. The original Request Builder requestBuilder = originalRequest. NewBuilder () / / a new Request. AddHeader ("Accept"."application/json")
.addHeader("Content-Type"."application/json; charset=utf-8")
.removeHeader("User-Agent")
.addHeader("User-Agent",BaseUtils.getUserAgent())
.method(originalRequest.method(),originalRequest.body());
// Log.e("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -",originalRequest.body().toString()); Request tokenRequest = null; // Request with tokenif (StringUtils.isEmpty(App.mmkv.decodeString(BaseConfig.TOKEN,null))){
return chain.proceed(requestBuilder.build());
}
tokenRequest = requestBuilder
.header("Authorization"."Bearer "+App.mmkv.decodeString(BaseConfig.TOKEN,null))
.build();
returnchain.proceed(tokenRequest); }}; Interceptor tokenRespInterceptor = newInterceptor() {// Intercept the return body to determine whether the token is expired. Override Public Response Intercept (Chain Chain) throws IOException {Request Request = chain.request(); Response response = chain.proceed(request); // KLog.d( response.body().string());if (isTokenExpired(response)){
KLog.d( "Automatically refresh Token and then request data again"); String newToken = getNewToken();if(newToken ! Request newRequest = chain.request().newBuilder().header()"Authorization"."Bearer "+ newToken) .build(); // request againreturnchain.proceed(newRequest); }}return response.newBuilder().body(ResponseBody.create(MediaType.parse("UTF-8"),body)).build(); }}; OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(DEFAULT_TIME_OUT, Timeunit.seconds) //l connection timeout duration. WriteTimeout (DEFAULT_READ_TIME_OUT, timeunit.seconds) // read/writeTimeout ReadTimeout (DEFAULT_READ_TIME_OUT, TimeUnit. SECONDS) / / read timeout retryOnConnectionFailure (trueAddNetworkInterceptor (tokenRespInterceptor) addNetworkInterceptor(tokenRespInterceptor) //.authenticator(authenticator) // Authorize authentication.build (); retrofit = new Retrofit.Builder() .client(client) .baseUrl(API.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build(); } /** * synchronizes the request mode to obtain the latest Token ** @return
*/
private String getNewToken() {// Through the token interface, GetRequest_Interface Request = new retrofit.builder ().baseurl (api.base_url) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build().create(GetRequest_Interface.class); RequestBody body = BaseUtils.convertJson(BaseUtils.paramsMap("refresh_token",App.mmkv.decodeString(BaseConfig.REFRESH_TOKEN,null)));
Call<RefreshToken> call = request.postRefreshToken(body);
try {
response = call.execute();
} catch (IOException e) {
e.printStackTrace();
}
KLog.e(response.body().getCode()+response.body().getMessage());
if (response.code() == 200 &&response.body().getCode() ==0){
newToken = response.body().getData().getToken();
KLog.e("-- -- -- -- -- -- -- -- -- -- -- -- -- -- --"+newToken);
App.mmkv.encode(BaseConfig.TOKEN,newToken);
App.mmkv.encode(BaseConfig.SCHOOL_TOKEN,response.body().getData().getSchool_token());
App.mmkv.encode(BaseConfig.EXPIRE_IN,response.body().getData().getExpire_in());
}
returnnewToken; } /** * Check whether Token is invalid ** @param Response * @return
*/
private boolean isTokenExpired(Response response) {
try {
body = response.body().string();
JSONObject object = new JSONObject(body);
message = object.getString("Message");
code = object.getInt("Code");
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
if ("Token is expired".equals(message)&& code == 1) {
return true;
}
return false; } private static class SingletonHolder{ private static final RetrofitHelper INSTANCE = new RetrofitHelper(); } /** * Get RetrofitServiceManager * @return
*/
public static RetrofitHelper getInstance() {returnSingletonHolder.INSTANCE; } /** * get the class of Service * @param Service * @param <T> * @return
*/
public <T> T create(Class<T> service){
returnretrofit.create(service); }}Copy the code
If there are multiple baseurls in the business, you can simply write a method to expose it.
A BaseObserver that processes the results of a request uniformly
- Start by creating a ResponseCallBack callback for the result of the request
public interface ResponseCallBack<T> {
void onSuccess(T t);
void onFault(String errorMsg);
}
Copy the code
- Create an initialization callback ProgressListener at the start of the request
public interface ProgressListener {
void startProgress();
void cancelProgress();
}
Copy the code
- Create a BaseObserver for unified processing of results
Create a BaseObserver for business processing in a callback
public class BaseObserver<T> implements Observer<T> { private ResponseCallBack responseCallBack; private ProgressListener progressListener; private Disposable disposable; public BaseObserver(ResponseCallBack responseCallBack,ProgressListener progressListener){ this.responseCallBack = responseCallBack; this.progressListener = progressListener; }}Copy the code
Initialization at the start of the request is done in the onSubscribe () method
@Override
public void onSubscribe(Disposable d) {
this.disposable = d;
if (progressListener != null){
progressListener.startProgress();
}
}
Copy the code
The request success business is handled in the onNext () method
@Override
public void onNext(T t) {
responseCallBack.onSuccess(t);
}
Copy the code
Handle request failure messages uniformly in the onError () method
@Override
public void onError(Throwable e) {
KLog.d(e.getMessage());
try {
if(e instanceof SocketTimeoutException) {responsecallBack.onfault ("Request timed out, please try again later.");
} else if(e instanceof ConnectException) {responsecallBack.onfault ("Network connection timed out. Please check network status.");
} else if(e instanceof SSLHandshakeException) {responsecallBack.onfault ("Security Certificate exception");
} else if(e instanceof HttpException) {int code = ((HttpException) e).code();if (code == 504) {
responseCallBack.onFault("Network exception, please check your network status.");
} else if (code == 404) {
responseCallBack.onFault("Requested address not found");
} else {
responseCallBack.onFault("Request failed"); }}else if(e instanceof UnknownHostException) {responsecallback.onfault ("Domain name resolution failed");
} else {
responseCallBack.onFault("error:" + e.getMessage());
}
} catch (Exception e2) {
e2.printStackTrace();
} finally {
Log.e("OnSuccessAndFaultSub"."error:" + e.getMessage());
if(disposable ! =null && ! Disposable. IsDisposed () {// Event completion cancel subscription disposable. Dispose (); }if(progressListener! =null){ progressListener.cancelProgress(); }}}Copy the code
Handle the business after the request completes successfully in onComplete ()
@Override
public void onComplete() {
if(disposable ! =null && ! Disposable. IsDisposed () {// Event completion cancel subscription disposable. Dispose (); }if (progressListener!=null){
progressListener.cancelProgress();
}
}
Copy the code
The code is as follows:
/**
* Created by darryrzhong
*
*/
public class BaseObserver<T> implements Observer<T> {
private ResponseCallBack responseCallBack;
private ProgressListener progressListener;
private Disposable disposable;
public BaseObserver(ResponseCallBack responseCallBack,ProgressListener progressListener){
this.responseCallBack = responseCallBack;
this.progressListener = progressListener;
}
@Override
public void onSubscribe(Disposable d) {
this.disposable = d;
if(progressListener ! = null){ progressListener.startProgress(); } } @Override public void onNext(T t) { responseCallBack.onSuccess(t); } @Override public void onError(Throwable e) { KLog.d(e.getMessage()); try {if(e instanceof SocketTimeoutException) {responsecallBack.onfault ("Request timed out, please try again later.");
} else if(e instanceof ConnectException) {responsecallBack.onfault ("Network connection timed out. Please check network status.");
} else if(e instanceof SSLHandshakeException) {responsecallBack.onfault ("Security Certificate exception");
} else if(e instanceof HttpException) {int code = ((HttpException) e).code();if (code == 504) {
responseCallBack.onFault("Network exception, please check your network status.");
} else if (code == 404) {
responseCallBack.onFault("Requested address not found");
} else {
responseCallBack.onFault("Request failed"); }}else if(e instanceof UnknownHostException) {responsecallback.onfault ("Domain name resolution failed");
} else {
responseCallBack.onFault("error:" + e.getMessage());
}
} catch (Exception e2) {
e2.printStackTrace();
} finally {
Log.e("OnSuccessAndFaultSub"."error:" + e.getMessage());
if(disposable ! =null && ! Disposable. IsDisposed () {// Event completion cancel subscription disposable. Dispose (); }if(progressListener! =null){ progressListener.cancelProgress(); } } } @Override public voidonComplete() {
if(disposable ! =null && ! Disposable. IsDisposed () {// Event completion cancel subscription disposable. Dispose (); }if(progressListener! =null){ progressListener.cancelProgress(); }}}Copy the code
At this point, the BaseObserver for unified processing results is wrapped
Others (request parameter pass, return JSON)
This varies depending on how the server receives data. To learn more about how to pass parameters, read Retrofit’s annotations for yourself. This is just about passing Json data to the server.
- First let’s look at the standard Json data format
Return body: {"Code": 0."Data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC*********"."refresh_token": "c9ced011-***************************"."expire_in": 1560330991,
"student_id": 33},"Message": "Login successful"} Request body: {"phone":"13145214436"."id":"12345"}
Copy the code
How to pass the request body
Let’s start by treating the outermost {} JSON object as a map object, so that you know how to convert it. Yes, that’s what you think.
HashMap<String, Object> map = new HashMap<>();
map.put("phone"."13145214436");
map.put("id"."12345");
Copy the code
The map object is then converted into a JSON string and passed to the server
Gson gson=new Gson();
String strEntity = gson.toJson(map);
RequestBody body = RequestBody.create(okhttp3.MediaType.parse("application/json; charset=UTF-8"),strEntity);
Copy the code
The same is true for more complex request bodies
{
"school_id": 1,"student_id": 23."start_time":"The 2019-05-10 15:04:05"."end_time":"The 2019-06-10 15:04:05"."points": [{"longitude": 0."latitude": 0}, {"longitude": 0."latitude": 0}, {"longitude": 0."latitude": 0}, {"longitude": 0."latitude": 0}]}Copy the code
We have a json array in the body of the request, and we have a nested JSON object in the body of the request, so again, think of the JSON array as a list.
Use the sample
Here is a simple demonstration of the login service
GetRequest_Interface request = RetrofitHelper.getInstance().create(GetRequest_Interface.class); // Request entry HashMap<String,Object> params = new HashMap(); params.put("phone",phone);
params.put("id",id); RequestBody body = BaseUtils.convertJson(params); Observable<RegistLogin> observable= request.postRegist(body); observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new BaseObserver<RegistLogin>(new ResponseCallBack<RegistLogin>() { @Override public void onSuccess(RegistLogin registLogin) @override public void onFault(String errorMsg) {baseutils. showmsg (mContext,errorMsg); } }, newProgressListener() {
@Override
public void startProgress() {
dialog = BaseUtils.showSpotsDialog(mContext,"Logging in");
dialog.show();
}
@Override
public void cancelProgress() { dialog.dismiss(); }}));Copy the code
In this way, the code is not clear and concise, code quality significantly improved a level
At this point,Rxjava and Retrofit combined use and encapsulation is basically complete, again, there is no perfect encapsulation, only the most suitable for your business encapsulation, so, if necessary, please customize your own business, here is only the idea.
Welcome to darryrzhong, more dry goods waiting for you to get yo.
A little red heart, please! Because your encouragement is the biggest power that I write!
Please pay attention to more wonderful articles
- Personal blog: Darryrzhong
- The Denver nuggets
- Jane’s book
- SegmentFault
- Moocs Notebook