2021年1月18號補(bǔ)充
最近遇到一個5.0以下的設(shè)備無法請求問題,主要的原因都是Okhttp4不支持5.0以下的設(shè)備猖辫,這邊我們需要做下兼容酥泞,不過我的設(shè)想是通過Gradle去做兼容,暫時還沒有弄出來啃憎≈ザ冢可以看下這個博客:
https://blog.csdn.net/mr_tony/article/details/108776208
2021年8月19號
這個博客寫了很久了,陸續(xù)還有人在點(diǎn)贊羡藐,我還是比較高興的仆嗦,個人覺得ResponseTransformer那一塊代碼可能有點(diǎn)晦澀難懂瘩扼,有些讀者也問過我這個變化的問題邢隧,如果參考我的代碼覺得可以優(yōu)化掉那一塊代碼我覺得也行倒慧,畢竟代碼寫得簡潔和可維護(hù)最好纫谅。ResposeTransformer那邊實(shí)際上用的是Rxjava的一種變換付秕,主要是我好久不用Rxjava了询吴,突然看了下這塊代碼猛计,自己也有點(diǎn)懵逼奉瘤,不過代碼是可以跑通的盗温,而且也解釋了這個變換卖局,我覺得如果Rxjava懂的人應(yīng)該能看懂砚偶。謝謝大家支持蟹演。
因為Retrofit方便使用和支持RxJava酒请,所以Retrofit已經(jīng)成為了非常流行的網(wǎng)絡(luò)框架羞反。學(xué)會封裝和使用Retrofit網(wǎng)絡(luò)請求框架練練手是提升自己架構(gòu)水平一個非常好的示例昼窗。而且當(dāng)成功封裝第一個組件時澄惊,再次遇到需要封裝組件這樣的任務(wù)也會變得得心應(yīng)手掸驱。
1毕贼、封裝的主要邏輯
根據(jù)這個邏輯圖一步一步封裝網(wǎng)絡(luò)框架吧鬼癣。
1.1 導(dǎo)入依賴
compile "io.reactivex.rxjava2:rxjava:2.1.0" // 必要rxjava2依賴
compile "io.reactivex.rxjava2:rxandroid:2.0.1" // 必要rxandrroid依賴待秃,切線程時需要用到
compile 'com.squareup.retrofit2:retrofit:2.3.0' // 必要retrofit依賴
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // 必要依賴锥余,和Rxjava結(jié)合必須用到驱犹,下面會提到
compile 'com.squareup.retrofit2:converter-gson:2.3.0' // 必要依賴雄驹,解析json字符所用
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1' //非必要依賴医舆, log依賴蔬将,如果需要打印OkHttpLog需要添加
1.2 新建Manger類霞怀,管理所需要的API
這里是一個千篇一律的套路,如果封裝全局使用的框架廉沮,并且需要和整個軟件有一樣長的生命周期滞时。那就有兩個特點(diǎn):
- 在Application里面初始化坪稽。
- 使用單例模式窒百。
- 在一個"管理類"中初始化所需要的所有參數(shù)贝咙。
/**
* Created by Zaifeng on 2018/2/28.
* API初始化類
*/
public class NetWorkManager {
private static NetWorkManager mInstance;
private static Retrofit retrofit;
private static volatile Request request = null;
public static NetWorkManager getInstance() {
if (mInstance == null) {
synchronized (NetWorkManager.class) {
if (mInstance == null) {
mInstance = new NetWorkManager();
}
}
}
return mInstance;
}
/**
* 初始化必要對象和參數(shù)
*/
public void init() {
// 初始化okhttp
OkHttpClient client = new OkHttpClient.Builder()
.build();
// 初始化Retrofit
retrofit = new Retrofit.Builder()
.client(client)
.baseUrl(Request.HOST)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static Request getRequest() {
if (request == null) {
synchronized (Request.class) {
request = retrofit.create(Request.class);
}
}
return request;
}
}
這里新建了NetWorkManager 在init方法中初始化了OkHttp和Retrofit。兩者的初始化都是構(gòu)造者模式陈症。
初始化OKHttp的拓展
上面的代碼只是添加了必要的屬性趴腋,還可以對OkHttp和Retrofit添加更多的拓展论咏。比如:需要對OkHttp添加Log可以如下寫厅贪。
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
addInterceptor()
還有很多的用法葵硕,比如:封裝一些公共的參數(shù)等等。參考如下博客:http://blog.csdn.net/jdsjlzx/article/details/52063950
這里就不多說明蜀变。
初始化Retrofit的時候的兩個必要配置:
- 第一個配置:
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
這個是用來決定你的返回值是Observable還是Call库北。
// 使用call的情況
Call<String> login();
// 使用Observable的情況
Observable<String> login();
如果返回為Call那么可以不添加這個配置贤惯。如果使用Observable那就必須添加這個配置棒掠。否則就會請求的時候就會報錯烟很!
對于這個配置雾袱,詳細(xì)可以看下這篇博客芹橡。
http://blog.csdn.net/new_abc/article/details/53021387
- 第二個配置:
.addConverterFactory(GsonConverterFactory.create())
這個配置是將服務(wù)器返回的json字符串轉(zhuǎn)化為對象林说。這個是可以自定義Converter來應(yīng)對服務(wù)器返回的不同的數(shù)據(jù)的腿箩。具體如何去自定義劣摇,這里也不做說明了(因為內(nèi)容比較多)钧惧,可以看下如下的博客浓瞪。
http://www.reibang.com/p/5b8b1062866b
初始化Request
這里也初始化了Request追逮。如果有Retrofit基礎(chǔ)看到retrofit.create
這個方法钮孵,就應(yīng)該知道Request是定義的請求的服務(wù)器的API封裝類巴席。里面通過注解的方式聲明所需要請求的接口漾唉,這里下面會講到分衫。
1.3 約定Response
在解析服務(wù)器返回的數(shù)據(jù)時般此,需要和服務(wù)器統(tǒng)一返回Json格式邀桑,這個在開發(fā)中經(jīng)常遇到科乎。一般服務(wù)器也不會隨便返回Json格式給前端捏萍,否則前端解析時將會非常麻煩玉吁。(所以這里的數(shù)據(jù)結(jié)構(gòu)需要前端和服務(wù)端約定一個固定的格式)
這里的固定json格式是:
{ret:0,data:"",msg:""}
所以這里定義的固定的Response為:
public class Response<T> {
private int ret; // 返回的code
private T data; // 具體的數(shù)據(jù)結(jié)果
private String msg; // message 可用來返回接口的說明
public int getCode() {
return ret;
}
public void setCode(int code) {
this.ret = code;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
1.4 定義Request
在前面NetWorkManager中已經(jīng)提到了这揣,這個是定義的請求服務(wù)器的API的接口。
public interface Request {
// 填上需要訪問的服務(wù)器地址
public static String HOST = "https://www.xxx.com/app_v5/";
@POST("?service=sser.getList")
Observable<Response<List<javaBean>>> getList(@Query("id") String id);
}
這里用@Post注解了一個Post請求片迅。
1.5 定義ResponseTransformer處理數(shù)據(jù)和異常
這個也是自己定義的類柑蛇,不使用這個自定義的類也是可以的耻台。但是為了封裝之后使用的便捷性蹋砚,還是建議封裝坝咐。ResponseTransformer實(shí)際上是Rxjava中的“變換”的封裝墨坚。
沒使用ResponseTransformer時的情況浦楣。
model.getCarList("xxxxx")
.compose(schedulerProvider.applySchedulers())
.subscribe(new Consumer<Response<List<JavaBean>>>() {
@Override
public void accept(Response<List<JavaBean>> listResponse) throws Exception {
if(listResponse.getCode() == 200){
List<JavaBean> javaBeans = listResponse.getData();
}else{
// 異常處理
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 異常處理
}
});
使用了ResponseTransformer的情況:
model.getCarList("xxxxx")
.compose(ResponseTransformer.handleResult())
.compose(schedulerProvider.applySchedulers())
.subscribe(new Consumer<List<JavaBean>>() {
@Override
public void accept(List<JavaBean> carBeans) throws Exception {
// 處理數(shù)據(jù) 直接獲取到List<JavaBean> carBeans
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
// 處理異常
}
}
);
沒有使用ResponseTransformer振劳,需要先判斷Response的code寸癌,然后將數(shù)據(jù)從Response中取出來蒸苇。比較繁瑣的就是判斷code==200的代碼吮旅,每次請求接口都需要去寫一遍溪烤。
使用了ResponseTransformer將解析的Response數(shù)據(jù)進(jìn)一步處理(將判斷code==200的代碼進(jìn)行封裝),直接將需要使用的數(shù)據(jù)處理提取出來庇勃,并且將Exception統(tǒng)一處理檬嘀!這個就是RxJava中“變換”的妙用,同時在使用時會和“compose”聯(lián)用责嚷,減少重復(fù)代碼鸳兽。
具體的ResponseTransformer代碼。
public class ResponseTransformer {
public static <T> ObservableTransformer<Response<T>, T> handleResult() {
return upstream -> upstream
.onErrorResumeNext(new ErrorResumeFunction<>())
.flatMap(new ResponseFunction<>());
}
/**
* 非服務(wù)器產(chǎn)生的異常罕拂,比如本地?zé)o無網(wǎng)絡(luò)請求揍异,Json數(shù)據(jù)解析錯誤等等全陨。
*
* @param <T>
*/
private static class ErrorResumeFunction<T> implements Function<Throwable, ObservableSource<? extends Response<T>>> {
@Override
public ObservableSource<? extends Response<T>> apply(Throwable throwable) throws Exception {
return Observable.error(CustomException.handleException(throwable));
}
}
/**
* 服務(wù)其返回的數(shù)據(jù)解析
* 正常服務(wù)器返回數(shù)據(jù)和服務(wù)器可能返回的exception
*
* @param <T>
*/
private static class ResponseFunction<T> implements Function<Response<T>, ObservableSource<T>> {
@Override
public ObservableSource<T> apply(Response<T> tResponse) throws Exception {
int code = tResponse.getCode();
String message = tResponse.getMsg();
if (code == 200) {
return Observable.just(tResponse.getData());
} else {
return Observable.error(new ApiException(code, message));
}
}
}
}
1.6渡处、 處理Exception
這里的Exception分為兩部分:
- 自己本地產(chǎn)生的Exception,比如解析錯誤,網(wǎng)絡(luò)鏈接錯誤等等。
- 服務(wù)器產(chǎn)生的Excption,比如404,503等等服務(wù)器返回的Excption。
定義ApiException統(tǒng)一處理:
public class ApiException extends Exception {
private int code;
private String displayMessage;
public ApiException(int code, String displayMessage) {
this.code = code;
this.displayMessage = displayMessage;
}
public ApiException(int code, String message, String displayMessage) {
super(message);
this.code = code;
this.displayMessage = displayMessage;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDisplayMessage() {
return displayMessage;
}
public void setDisplayMessage(String displayMessage) {
this.displayMessage = displayMessage;
}
}
對于本地產(chǎn)生的Exception如下處理:
public class CustomException {
/**
* 未知錯誤
*/
public static final int UNKNOWN = 1000;
/**
* 解析錯誤
*/
public static final int PARSE_ERROR = 1001;
/**
* 網(wǎng)絡(luò)錯誤
*/
public static final int NETWORK_ERROR = 1002;
/**
* 協(xié)議錯誤
*/
public static final int HTTP_ERROR = 1003;
public static ApiException handleException(Throwable e) {
ApiException ex;
if (e instanceof JsonParseException
|| e instanceof JSONException
|| e instanceof ParseException) {
//解析錯誤
ex = new ApiException(PARSE_ERROR, e.getMessage());
return ex;
} else if (e instanceof ConnectException) {
//網(wǎng)絡(luò)錯誤
ex = new ApiException(NETWORK_ERROR, e.getMessage());
return ex;
} else if (e instanceof UnknownHostException || e instanceof SocketTimeoutException) {
//連接錯誤
ex = new ApiException(NETWORK_ERROR, e.getMessage());
return ex;
} else {
//未知錯誤
ex = new ApiException(UNKNOWN, e.getMessage());
return ex;
}
}
}
這些異常如何拋出呢 改含? 那就看下上面提到的 ResponseTransformer 中處理異常的代碼吧。兩種異常都通過Observable.error發(fā)射出去盗扇。
// 本地異常處理
Observable.error(CustomException.handleException(throwable));
// 對服務(wù)器放回的code進(jìn)行判斷
int code = tResponse.getCode();
String message = tResponse.getMsg();
if (code == 200) {
return Observable.just(tResponse.getData());
} else {
return Observable.error(new ApiException(code, message));
}
1.7斑鼻、線程切換
使用過Rxjava對Rxjava線程切換應(yīng)該比較熟悉荒叶,這里將線程切換單獨(dú)封裝成一個類再結(jié)合compose使用。
public class SchedulerProvider implements BaseSchedulerProvider {
@Nullable
private static SchedulerProvider INSTANCE;
// Prevent direct instantiation.
private SchedulerProvider() {
}
public static synchronized SchedulerProvider getInstance() {
if (INSTANCE == null) {
INSTANCE = new SchedulerProvider();
}
return INSTANCE;
}
@Override
@NonNull
public Scheduler computation() {
return Schedulers.computation();
}
@Override
@NonNull
public Scheduler io() {
return Schedulers.io();
}
@Override
@NonNull
public Scheduler ui() {
return AndroidSchedulers.mainThread();
}
@NonNull
@Override
public <T> ObservableTransformer<T, T> applySchedulers() {
return observable -> observable.subscribeOn(io())
.observeOn(ui());
}
}
2、實(shí)戰(zhàn)
封裝好之后,接下來就是實(shí)際使用了葬项。不過在實(shí)際開發(fā)中可能是先實(shí)現(xiàn)邏輯再去封裝,很多東西需要在代碼寫完之后才知道要去封裝起來的。
2.1、初始化
BaseApplication中初始化摊唇。
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
NetWorkManager.getInstance().init();
}
}
2.2岛请、搭建好MVP結(jié)構(gòu)缩膝,在Presenter中使用。
Disposable disposable = model.getCarList("xxxxxx")
.compose(ResponseTransformer.handleResult())
.compose(schedulerProvider.applySchedulers())
.subscribe(carBeans -> {
// 處理數(shù)據(jù) 直接獲取到List<JavaBean> carBeans
view.getDataSuccess();
}, throwable -> {
// 處理異常
view.getDataFail();
});
mDisposable.add(disposable);
兩個回調(diào)井厌,回調(diào)之后View層做出相應(yīng)的UI變化即可拳魁。
這里用了compose的使用就不多做解釋授舟。這里compose結(jié)合線程的切換封裝和變換封裝奢啥,減少了冗余代碼柬姚。
關(guān)于compose的鏈接:http://www.reibang.com/p/3d0bd54834b0
最后的目錄結(jié)構(gòu):
3、需要注意的地方
1昧绣、很多時候需要實(shí)現(xiàn)效果才能再去進(jìn)行封裝税灌。
2狸窘、在封裝框架的時候封裝的是邏輯層牛哺,不要在邏輯層中寫業(yè)務(wù)層的代碼淳附。