网上都说Retrofit+Rxjava 也又说RxAndroid的 这种网络请求框架好,也有很多实例,但单个的例子毕竟是单个的例子,用到具体的项目里面有成百上千个接口,直接把例子套过来不一定就合适了,需要进行一定的封装用起来才方便。不知道各位高手是怎么用的,我先说说我的简单的封装算是抛砖引玉了,有什么不对的地方欢迎大家批评指正。
1.HttpManager
package com.example.main.net.manager;
import com.example.main.base.BaseApplication;
import com.example.main.finance.bean.AppData;
import com.example.main.net.service.HttpService;
import com.example.main.utils.Constants;
import com.example.main.utils.MD5;
import com.example.main.utils.log.okHttpLog.HttpLoggingInterceptorM;
import com.example.main.utils.log.okHttpLog.LogInterceptor;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import okhttp3.HttpUrl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
/**
* Created by zhougang on 2016/10/19.
*/
public class HttpManager {
//public static final String BASE_URL = "http://hltm-api.tomoya.cn";
//请求金融
public static final String BASE_URL = Constants.JINRONG_ROOT;
//短缓存有效期为1分钟
// public static final int CACHE_STALE_SHORT = 60;
//长缓存有效期为7天
public static final int CACHE_STALE_LONG = 60 * 60 * 24 * 7;
//public static final String CACHE_CONTROL_AGE = "Cache-Control: public, max-age=";
//查询缓存的Cache-Control设置,为if-only-cache时只查询缓存而不会请求服务器,max-stale可以配合设置缓存失效时间
// public static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_LONG;
//查询网络的Cache-Control设置,头部Cache-Control设为max-age=0时则不会使用缓存而请求服务器
// public static final String CACHE_CONTROL_NETWORK = "max-age=0";
private static OkHttpClient mOkHttpClient;
private volatile static HttpManager INSTANCE;
private static HttpService sApiService;
//设置连接超时的值
private static final int TIMEOUT = 20;
private HttpManager() {
initOkHttpClient();
initRetrofit();
}
private void initRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(mOkHttpClient)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
sApiService = retrofit.create(HttpService.class);
}
public static HttpManager getHttpManager() {
if (null == INSTANCE) {
synchronized (HttpManager.class) {
if (null == INSTANCE) {
INSTANCE = new HttpManager();
}
}
}
return INSTANCE;
}
public HttpService getHttpService() {
return sApiService;
}
private void initOkHttpClient() {
// 日志输出拦截器
// HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
// loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 格式化的日志输出
HttpLoggingInterceptorM formattedLoggingInterceptor = new HttpLoggingInterceptorM(new LogInterceptor("OkHttp"));
formattedLoggingInterceptor.setLevel(HttpLoggingInterceptorM.Level.BODY);
if (mOkHttpClient == null) {
synchronized (HttpManager.class) {
if (mOkHttpClient == null) {
//application/json;
mOkHttpClient = new OkHttpClient.Builder()
.addInterceptor(addHeader) //Header
// .sslSocketFactory(SSLHelper.getSSLCertifcation(BaseApplication.get_context()))//为OkHttp对象设置SocketFactory用于双向认证
.addInterceptor(formattedLoggingInterceptor) // 通过配置拦截器 :在转换成对象前对json字符串进行监控
.addInterceptor(addQueryParameterInterceptor) //公共参数
.retryOnConnectionFailure(true)
.readTimeout(TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(TIMEOUT, TimeUnit.SECONDS)
.connectTimeout(TIMEOUT, TimeUnit.SECONDS)
.build();
}
}
}
}
//Header
Interceptor addHeader = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request request;
request = originalRequest.newBuilder()
.addHeader("Accept", "application/json")
.build();
return chain.proceed(request);
}
};
//公共参数
Interceptor addQueryParameterInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request request;
HttpUrl modifiedUrl = originalRequest.url().newBuilder()
.addQueryParameter("tokenId", getToken())
.addQueryParameter("sign", getDefaultToken())
.addQueryParameter("clientType", getClientType())
.addQueryParameter("version", getVersion())
.addQueryParameter("timestampApp", str_timeStamp)
.build();
request = originalRequest.newBuilder().url(modifiedUrl).build();
return chain.proceed(request);
}
};
public Map<String, String> getDefoautParame() {
Map<String, String> map = new HashMap<String, String>();
map.put("tokenId", getToken());
map.put("clientType", getClientType());
map.put("version", getVersion());
map.put("timestampApp", getTimeStamp());
return map;
}
private String token_id = "";
private String str_timeStamp = "";
private String getToken() {
token_id = BaseApplication.getApplication().getUserInfo().getTokenId();
return token_id;
}
//tokenId 放在header里面
Interceptor mTokenInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
Request authorised = originalRequest.newBuilder()
.header("tokenId", getToken())
.build();
return chain.proceed(authorised);
}
};
private String getClientType() {
return AppData.getInstance().getPlatformName();
}
private String getVersion() {
return AppData.getInstance().getVersiontName();
}
private String getTimeStamp() {
return AppData.getInstance().getTimeData();
}
}
2. HttpService
package com.example.main.net.service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
import rx.Observable;
/**
* Created by zhoguang on 2016/11/24.
*/
public interface HttpService {
@GET("member/userLogin")
Observable<RXBase<LoginBean>> login(@QueryMap Map<String, String> map);
// 获取产品列表
@POST("product/list")
Observable<RXBase<ProductListBean>> getLiCaiList(@Query("pageNum") String pageNo, @Query("pageSize") String pageSize);
// 获取产品详情
@GET("product/details")
Observable<RXBase<ProductDetailBean>> getLiCaiDetail(@Query("id") String ygbMoneyflowId);
// 获取状态
@GET("fproductApp/ygbMoneyflow/isNew")
Observable<RXBase<String>> isInvest(@QueryMap Map<String, String> params);
}
3. RXBase
/**
*
* 使用 RxJava+Retrofit网络框架,接收返回值的公用JavaBean
*
* 调试一个接口的几个步骤
* 以获取消息列表为例
*
* 1.创建 BMessage (BMessage 只需要包含json串datas字段的的属性(或data数组里的取一个对象的属性)
*
* 2.在HttpService里添加请求url:
* @GET("a/jlwnotice/jlwNotice/selectNotice")
* Observable<RXBase<BMessage>> getMessage(@Query("userid") String userId);
*
* 3.在MessageActivity里面发起请求:
* HttpManager.getHttpManager().getHttpService().getMessage(userInfo.getUserId())
* .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
* .subscribe(new BaseSubscriber<RXBase<BMessage>>(MassageActivity.this) {
* @Override
* public void onNext(RXBase<BMessage> rxBase) {
* ArrayList<BMessage> list = rxBase.getData();
* if(rxBase.isSuccess() && list!=null && list.size()>0){
* listMessageItem.addAll(list);
* mAdapter.notifyDataSetChanged();
* }
* }
* });
*
*
*
* @author zhougang
* @date 2016/12/22 16:36
*
*/
public class RXBase <T> {
/**
* 是否成功
*/
protected boolean isSuccess;
/**
* 返回码
*/
protected int rtnCode;
protected String rtnMsg;
protected T datas;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
public int getRtnCode() {
return rtnCode;
}
public void setRtnCode(int rtnCode) {
this.rtnCode = rtnCode;
}
public String getRtnMsg() {
return rtnMsg;
}
public void setRtnMsg(String rtnMsg) {
this.rtnMsg = rtnMsg;
}
public T getDatas() {
return datas;
}
}
4.具体的业务Bean(和接口返回的son数据datas节点内的数据对应的),我举个例子,这个接口是获取理财产品列表,我需要一个Bean存放。
public class InvestmentBean {
private boolean isNewRecord;
private String contractId;
private String contractNumber;
private String contractName;
private String userId;
private String partyA;
private String guarantor;
private String contractType;
private int productId;
private String productName;
private String startTime;
private String endTime;
private String returnRate;
private String totalMoney;
private String surplusMoney;
private String contractTime;
private String contractStatus;
private String contractSort;
private String orderType;
public boolean isIsNewRecord() {
return isNewRecord;
}
public void setIsNewRecord(boolean isNewRecord) {
this.isNewRecord = isNewRecord;
}
public String getContractId() {
return contractId;
}
public void setContractId(String contractId) {
this.contractId = contractId;
}
public String getContractNumber() {
return contractNumber;
}
public void setContractNumber(String contractNumber) {
this.contractNumber = contractNumber;
}
public String getContractName() {
return contractName;
}
public void setContractName(String contractName) {
this.contractName = contractName;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPartyA() {
return partyA;
}
public void setPartyA(String partyA) {
this.partyA = partyA;
}
public String getGuarantor() {
return guarantor;
}
public void setGuarantor(String guarantor) {
this.guarantor = guarantor;
}
public String getContractType() {
return contractType;
}
public void setContractType(String contractType) {
this.contractType = contractType;
}
public int getProductId() {
return productId;
}
public void setProductId(int productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getStartTime() {
return startTime;
}
public void setStartTime(String startTime) {
this.startTime = startTime;
}
public String getEndTime() {
return endTime;
}
public void setEndTime(String endTime) {
this.endTime = endTime;
}
public String getReturnRate() {
return returnRate;
}
public void setReturnRate(String returnRate) {
this.returnRate = returnRate;
}
public String getTotalMoney() {
return totalMoney;
}
public void setTotalMoney(String totalMoney) {
this.totalMoney = totalMoney;
}
public String getSurplusMoney() {
return surplusMoney;
}
public void setSurplusMoney(String surplusMoney) {
this.surplusMoney = surplusMoney;
}
public String getContractTime() {
return contractTime;
}
public void setContractTime(String contractTime) {
this.contractTime = contractTime;
}
public String getContractStatus() {
return contractStatus;
}
public void setContractStatus(String contractStatus) {
this.contractStatus = contractStatus;
}
public String getContractSort() {
return contractSort;
}
public void setContractSort(String contractSort) {
this.contractSort = contractSort;
}
public String getOrderType() {
return orderType;
}
public void setOrderType(String orderType) {
this.orderType = orderType;
}
}
5.BaseSubscriber
import android.content.Context;
import android.widget.Toast;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import rx.Subscriber;
/**
* Created by zhougang on 2016/11/24.
*/
public abstract class BaseSubscriber<T> extends Subscriber<T> {
Context context;
public BaseSubscriber( Context context) {
this.context = context;
}
@Override
public void onStart() {
// Toast.makeText(context,"开始请求数据...",Toast.LENGTH_LONG).show();
}
/**
* 完成
*/
@Override
public void onCompleted() {
// Toast.makeText(context, "onCompleted()....", Toast.LENGTH_SHORT).show();
}
/**
* 对错误进行统一处理
* @param e
*/
@Override
public void onError(Throwable e) {
if (e instanceof SocketTimeoutException) {
Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
} else if (e instanceof ConnectException) {
Toast.makeText(context, "网络中断,请检查您的网络状态", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
@Override
public abstract void onNext(T t);
}
6.Activity里调用
public void getListData(final int page) {
showViewLoading(true);
Map<String, String> map = new HashMap<>();
map.put("productId", productId);
map.put("pageNo", page + "");
map.put("pageSize", "20");
HttpManager.getHttpManager().getHttpService() .getFinanceInvestList(map) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber<RXBase<ArrayList<InvestmentBean>>>(InvestmentHistoryActivity.this) {
@Override
public void onNext(RXBase<ArrayList<InvestmentBean>> bean) {
showViewLoading(false);
investment_refresh.refreshSuccess();
if (bean.getRtnCode() == 100) {
list.addAll(bean.getDatas());
} else {
ToastUtil.ShowToastCommon(InvestmentHistoryActivity.this, bean.getRtnMsg(), 1);
}
}
@Override
public void onError(Throwable e) {
showViewLoading(false);
super.onError(e);
}
});
}
6. HttpManager的一些处理技巧
我给我的OkHttpClient添加了一些拦截器:
.addInterceptor(formattedLoggingInterceptor) // 通过配置拦截器 :在转换成对象前对json字符串进行监控,打印返回数据
.addInterceptor(addQueryParameterInterceptor) //公共参数
刚开始使用Retrofit最烦恼的就是log里面看不到返回的数据,只能调试看,很麻烦,后来知道了可以使用拦截器打印日志。这里面我为了更方便的查看日志,我使用了大神提供
的一个带格式的json日志打印的控件Android 网络框架 HttpLoggingInterceptor优化
另外,有的地方服务器端由于种种原因返回的json有点小瑕疵,不符合resultful要求,服务器端说改起来有困难,我也是个不愿意勉强被人的人,与其多费口舌,不如自己修改一下。
private void initRetrofit() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(mOkHttpClient)
.addConverterFactory(CustomGsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
sApiService = retrofit.create(HttpService.class);
}
package com.example.main.net.manager;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public final class CustomGsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static CustomGsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static CustomGsonConverterFactory create(Gson gson) {
return new CustomGsonConverterFactory(gson);
}
private final Gson gson;
private CustomGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonRequestBodyConverter<>(gson, adapter);
}
}
CustomGsonRequestBodyConverter
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public final class CustomGsonConverterFactory extends Converter.Factory {
/**
* Create an instance using a default {@link Gson} instance for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static CustomGsonConverterFactory create() {
return create(new Gson());
}
/**
* Create an instance using {@code gson} for conversion. Encoding to JSON and
* decoding from JSON (when no charset is specified by a header) will use UTF-8.
*/
public static CustomGsonConverterFactory create(Gson gson) {
return new CustomGsonConverterFactory(gson);
}
private final Gson gson;
private CustomGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter<?, RequestBody> requestBodyConverter(Type type,
Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonRequestBodyConverter<>(gson, adapter);
}
}
CustomGsonResponseBodyConverter
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import java.io.IOException;
import okhttp3.ResponseBody;
import retrofit2.Converter;
/**
* 过滤掉不符合规定的json(很重要)
*
* Created by zhougang on 2017/1/9
*/
public class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter<T> adapter;
CustomGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
// JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
//正常情况下返回的可能是对象或是数组,没有值的情况下却返回一个空字符串,解析会有问题,这里做一下处理。将 ‘ "data":"",’ 直接去掉
String str1 = "\"data\":"+"\"\",";
String str2 = "";
return adapter.fromJson(value.string().replace(str1,str2).replace("\\","").replace("\"{","{").replace("}\"","}"));
} finally {
value.close();
}
}
}
几个需要注意到的地方注意到RXBase里面datas那个泛型,使得我们的datas 参数可以接收字符串,数组,对象。HttpService里面,调用的时候都要注意这个Obsavable里面的参数类型。