每個App都避免不了要進行網(wǎng)絡請求媳谁,從最開始的用谷歌封裝的volley到再到android-async-http再到OKHttpUtils再到現(xiàn)在的Retrofit和RxJava,從我自己用后的體驗來看,用了retrofit和RxJava真的回不去了,回不去了司草,不去了授段,去了,了...(哈哈涩哟,本來還想分析下這四個的區(qū)別索赏,網(wǎng)上這樣的文章很多,我就沒必要多添亂了-.-)贴彼。不多逼逼潜腻,下面開始正文。
RxJava的學習資料
1器仗、Rxjava和Retrofit依賴導入:
compile 'io.reactivex:rxandroid:1.2.0' //Rxjava專門針對anroid封裝的RxAndroid
compile 'io.reactivex:rxjava:1.1.5'
compile 'com.squareup.retrofit2:retrofit:2.0.2' //retrofit
compile 'com.squareup.retrofit2:converter-gson:2.0.2' //gson converter
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2' //Retrofit專門為Rxjava封裝的適配器
compile 'com.google.code.gson:gson:2.6.2' //Gson
compile 'com.squareup.okhttp3:logging-interceptor:3.1.2' //打印網(wǎng)絡請求的log日志
2融涣、網(wǎng)絡請求基類的配置
建立一個工廠類
public class ServiceFactory {
private final Gson mGsonDateFormat;
public ServiceFactory(){
mGsonDateFormat = new GsonBuilder()
.setDateFormat("yyyy-MM-dd hh:mm:ss")
.create();
}
private static class SingletonHolder{
private static final ServiceFactory INSTANCE = new ServiceFactory();
}
public static ServiceFactory getInstance(){
return SingletonHolder.INSTANCE;
}
public <S> S createService(Class<S> serviceClass){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL) //Retrofit2 base url 必須是這種格式的:http://xxx.xxx/
.client(getOkHttpClient())--------------------------添加Gson工廠變換器也就是不用管數(shù)據(jù)解析-----------------------------------
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(serviceClass);
}
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
//打印retrofit日志
Log.i("RetrofitLog","retrofitBack ======================= "+message);
}
});
private static final long DEFAULT_TIMEOUT = 10;
private OkHttpClient getOkHttpClient(){
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
//定制OkHttp
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
builder.writeTimeout(DEFAULT_TIMEOUT,TimeUnit.SECONDS);
builder.addInterceptor(loggingInterceptor);
//設置緩存
File httpCacheDirectory = new File(SDCardUtils.getRootDirectoryPath(),"這里是你的網(wǎng)絡緩存存放的地址");
builder.cache(new Cache(httpCacheDirectory,10*1024*1024));
return builder.build();
}
}
好了 下一步 我們要建一個網(wǎng)絡請求的基類,一般網(wǎng)絡請求返回的數(shù)據(jù)最外層的根式就是 code msg result青灼,可變的就是result暴心,所以我們把result的類型定義為一個泛型的
public class HttpResult<T> extends BaseEntity {
public int code;
private boolean isSuccess;
private T result;
private String msg;
public void setMsg(String msg) {
this.msg = msg;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public boolean isSuccess() {
return code == 200;
}
public int getCode() {
return code;
}
}
既然我們的網(wǎng)絡請求是rxjava配合retrofit 下面就定義我們的網(wǎng)絡請求訂閱subscriber
public abstract class HttpResultSubscriber<T> extends Subscriber<HttpResult<T>> {
@Override
public void onNext(HttpResult<T> t) {
if (t.isSuccess()) {
onSuccess(t.getResult());
} else {
_onError(t.getMsg().getCode());
}
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
//在這里做全局的錯誤處理
if (e instanceof ConnectException ||
e instanceof SocketTimeoutException ||
e instanceof TimeoutException) {
//網(wǎng)絡錯誤
_onError(-9999);
}
}
public abstract void onSuccess(T t);
public abstract void _onError(int status);
}
OK我們的網(wǎng)絡請求基類已經完成啦!下面開始我們的網(wǎng)絡請求
首先我們定義一個接口(以登錄為例):
public interface LoginService {
//這個例子是post為例杂拨,如果想要了解其他的網(wǎng)絡請求专普,請點擊文章開始出的retrofit鏈接
@FormUrlEncoded
@POST(Constant.LOGIN_URL) 這里是你的登錄url//可以看到我們的登錄返回的是一個Observable,它里面包含的使我們的網(wǎng)絡請求返回的實體基類弹沽,
//而我們實體基類的result現(xiàn)在就是UserInfoEntity
Observable<HttpResult<UserInfoEntity>> login(@Field("mobile") String phone,
@Field("password") String pwd);
}
現(xiàn)在開始我們的網(wǎng)絡請求啦
public void login(String phone, String pwd) {
ServiceFactory.getInstance()
.createService(LoginService.class)
.login(phone,pwd)
.compose(TransformUtils.<HttpResult<UserInfoEntity>>defaultSchedulers())
.subscribe(new HttpResultSubscriber<UserInfoEntity>() {
@Override
public void onSuccess(UserInfoEntity userInfoEntity) {
//這是網(wǎng)絡請求陳宮的回調
}
@Override
public void _onError(int status) {
//這是失敗的回調 根據(jù)status做具體的操作
}
});
}
你以為這樣就行了?檀夹,? 這樣子確實沒毛病筋粗,確實已經妥妥的了≌ǘ桑可是娜亿,可是,事與愿違鞍龆隆B蚓觥!吼畏!
3督赤、具體解決辦法
一般情況這是我們的返回json格式:
{
"code":200,
"msg":"成功",
"result":{}
}
我們剛才定義登錄接口的時候 返回的實體基類例傳入的是UserInfoEntity? 這確實是沒問題的 可是你們加入登錄失敗的時候返回的json數(shù)據(jù)格式是這樣的怎么辦?
{
"code":300,
"msg":"成功",
"result":[]
}
失敗的時候返回的實體又是一個數(shù)組泻蚊,這樣子就會抱一個json解析異常拿不多失敗的狀態(tài)碼和提示信息
OK其實我們的網(wǎng)絡請求已經完成90%了躲舌,剩下的就是不重要的失敗的時候回調了。
方法一:(這是在后臺兄弟好說話性雄,而且不打人的情況下...當然這種好人没卸,還是有的,不過這不是我們今天要講的重點)
我們可以讓后臺返回的json數(shù)據(jù)中的result永遠是個數(shù)組秒旋。
{
"code":300,
"msg":"成功",
"result":[]
}
方法二:
??????? 首先給大家看一個圖片
上面我們添加的工廠變換器是導入的依賴 compile 'com.squareup.retrofit2:converter-gson:2.0.2' 這個提供的约计,
那可能有人要問了,那我們不用這個用哪個啊迁筛,不著急病蛉,不著急。還好retrofit是支持自定義的ConverterFactory的
下面我們就開始我們的自定義征程吧瑰煎。
---------------------------------------------------------華麗麗的分割線-----------------------------------------------
1、自定義Gson響應體變換器
public class GsonResponseBodyConverter<T> implements Converter<ResponseBody,T>{
private final Gson gson;
private final Type type;
public GsonResponseBodyConverter(Gson gson,Type type){
this.gson = gson;
this.type = type;
}
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
//先將返回的json數(shù)據(jù)解析到Response中俗孝,如果code==200酒甸,則解析到我們的實體基類中,否則拋異常
Response httpResult = gson.fromJson(response, Response.class);
if (httpResult.getCode()==200){
//200的時候就直接解析赋铝,不可能出現(xiàn)解析異常插勤。因為我們實體基類中傳入的泛型,就是數(shù)據(jù)成功時候的格式
return gson.fromJson(response,type);
}else {
ErrorResponse errorResponse = gson.fromJson(response,ErrorResponse.class);
//拋一個自定義ResultException 傳入失敗時候的狀態(tài)碼革骨,和信息
throw new ResultException(errorResponse.getCode(),errorResponse.getMsg());
}
}
}
2农尖、自定義一個響應變換工廠 繼承自 retrofit的 converter.Factory
public class ResponseConverterFactory extends Converter.Factory {
public static ResponseConverterFactory create() {
return create(new Gson());
}
public static ResponseConverterFactory create(Gson gson) {
return new ResponseConverterFactory(gson);
}
private final Gson gson;
private ResponseConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
//返回我們自定義的Gson響應體變換器
return new GsonResponseBodyConverter<>(gson, type);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
//返回我們自定義的Gson響應體變換器
return new GsonResponseBodyConverter<>(gson,type);
}
}
//然后將上面的GsonConverterFactory.create()替換成我們自定義的ResponseConverterFactory.create()
然后將上面的GsonConverterFactory.create() 替換成我們自定義的ResponseConverterFactory.create()。
public <S> S createService(Class<S> serviceClass){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.client(getOkHttpClient())
//.addConverterFactory(GsonConverterFactory.create())
//然后將上面的GsonConverterFactory.create()替換成我們自定義的ResponseConverterFactory.create()
.addConverterFactory(ResponseConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(serviceClass);
}
再然后良哲,最后一個然后啦(-.-)
在我們的自定義的Rxjava訂閱者 subscriber中的onError()中加入我們剛才定義的ResultException盛卡。
@Override
public void onError(Throwable e) {
e.printStackTrace();
//在這里做全局的錯誤處理
if (e instanceof ConnectException ||
e instanceof SocketTimeoutException ||
e instanceof TimeoutException) {
//網(wǎng)絡錯誤
_onError(-9999);
} else if (e instanceof ResultException) {
//自定義的ResultException
//由于返回200,300返回格式不統(tǒng)一的問題,自定義GsonResponseBodyConverter凡是300的直接拋異常
_onError(((ResultException) e).getErrCode());
System.out.println("---------errorCode------->"+((ResultException) e).getErrCode());
}
}
這次是真的完成了我們的json數(shù)據(jù)解析異常的處理筑凫,其實我們的解決辦法是解析了兩次滑沧,第一次解析的時候我們的Response中只有只是解析了最外層的 code 和 msg? 并村,result中的是沒有解析的。response中的code==200滓技,直接將數(shù)據(jù)解析到我們的實體基類中哩牍。如果code!=200時令漂,直接拋自定義的異常膝昆,直接會回調到subscriber中的onError()中。雖然進行了兩次解析叠必,但是第一次只是解析了code荚孵,和msg 對于效率的影響其實并不大,在功能實現(xiàn)的基礎上一點點效率的影響(而且這個影響是微乎其微的-.-)其實無傷大雅的挠唆。