Okhttp3+Rxjava+Retrofit2是目前在android中比较流行的一个网络框架,想起以前最开始用android原生Httpcient、URLConnection,随后使用的是Volley、Okhttp。网络框架一直在演变和升级。
今天我分享的是自己封装的Okhttp3+Rxjava+Retrofit2,其中主要功能如下:日志拦截器、Header加密(Token)、缓存策略、生命周期管理、网络加载弹窗等功能。
GitHub项目地址:https://github.com/hjy15759633702/TestHttp
以下是封装后代码:
/**
* 登录请求
* @author hjy
* created at 2017/12/10 11:51
*/
private void login() {
Map params = new HashMap<>();
params.put("account","test");
params.put("password","test");
Observable> observable = HttpManager.init().with(MainActivity.this).login(params);
observable.compose(RxSchedulersHelper.>doIoAndMain())
.compose(RxDataHelper.handResult(LifeCycleEvent.DESTROY,lifecycleSubject))
.subscribe(new RxSubscriber(MainActivity.this) {
@Override
protected void onSuccess(User user) {
Log.d(HttpConstant.TAG,user.toString());
}
@Override
protected void onError(String mess) {
Log.d(HttpConstant.TAG,mess.toString());
}
});
}
1、依赖
首先,要在build.gradle中添加相关的依赖,项目中用到Retrofit2,RxJava,Okhttp3,还用到Jackson、stetho、logger等。
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:retrofit-converters:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.3.0'
compile 'io.reactivex:rxjava:1.3.0'
compile 'io.reactivex:rxandroid:1.2.1'
2、定义Retrofit访问的接口
@FormUrlEncoded
@POST("login")
Observable<Result<User>> login(@FieldMap Map<String,String> params);
@GET("hot")
Observable<Result<String>> gethot(@QueryMap Map<String,String> params);
FormUrlEncoded指定表单形式提交,POST指定POST方式,括号是接口路径,与Retrofit.Builder的baseUrl拼接而成的。(注意:不能以 / 开头,因为baseUrl必须以 / 结尾)。其他注解方式请直接查看Retrofit2.0 官方文档
3、初始化Retrofit2
创建一个抽象类BaseRetrofit类,抽象类中含有一个抽象方法getBaseUrl和一个getOkHttpClient方法,其中getBaseUrl让子类实现获取url,getOkHttpClient获取OkHttpClient对象。
/**
* author:hjy
* date:2017/8/29 09:08
* detail:Retrofit基类
*/
public abstract class BaseRetrofit {
public Retrofit build()
{
Retrofit.Builder builder = new Retrofit.Builder();
builder.baseUrl(getBaseUrl())
.client(getOkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create());
return enrichBuilder(builder).build();
}
protected abstract String getBaseUrl();
// 默认值新建BaseOkhttpClient对象
protected OkHttpClient getOkHttpClient(){
return new BaseOkhttpClient().build();
}
protected Retrofit.Builder enrichBuilder(Retrofit.Builder builder) {
return builder;
}
}
4、初始化Okhttp3
创建BaseOkhttpClient基类,为了更好扩展,基类中公共方法richBuild让子类实现,进一步扩展OkHttpClient。扩展类有CacheOkhttpClient 带有数据缓存、HeaderOkhttpClient头部认证等,根据需求还可以扩展token、证书等。
BaseOkhttpClient.java
/**
* author:hjy
* date:2017/7/18 14:29
* detail:okhttpClient基类
*/
public class BaseOkhttpClient {
public OkHttpClient build()
{
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addNetworkInterceptor(new StethoInterceptor())
.retryOnConnectionFailure(true)
.connectTimeout(HttpConstant.CONNECT_TIME, TimeUnit.SECONDS)
.readTimeout(HttpConstant.READ_TIME, TimeUnit.SECONDS)
.writeTimeout(HttpConstant.WRITE_TIME, TimeUnit.SECONDS)
.build();
return richBuild(builder).build();
}
protected OkHttpClient.Builder richBuild(OkHttpClient.Builder builder) {return builder;}
}
CacheOkhttpClient.java
/**
* author:hjy
* date:2017/7/18 15:05
* detail:cacheOkhttpClient 带有数据缓存
*/
public class CacheOkhttpClient extends BaseOkhttpClient {
private Context mContext;
public CacheOkhttpClient(Context context) {
this.mContext = context;
}
@Override
protected OkHttpClient.Builder richBuild(OkHttpClient.Builder builder) {
File cacheFile = new File(mContext.getCacheDir(), mContext.getString(R.string.app_name));
Cache cache = new Cache(cacheFile, SIZE_OF_CACHE); //50Mb
// 没有网络直接读取本地且本地缓存时间为最大值,有网络直接网络获取,
// 如果想在有网络情况下,超过某个值进行请求,可以修改时间
// 以这些只有针对get请求,其他请求无效
builder.cache(cache).addNetworkInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (NetUtil.hashNetWork(mContext)) {
return response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", "public, max-age=" + MAX_CACHE_TIME)
.build();
} else {
return response.newBuilder()
.removeHeader("Pragma")
.header("Cache-Control", CacheControl.FORCE_CACHE.toString())
.build();
}
}
}).addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!NetUtil.hashNetWork(mContext)) {
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE)
.build();
}
return chain.proceed(request);
}
});
return super.richBuild(builder);
}
}
HeaderOkhttpClient.java
/**
* author:hjy
* date:2017/8/28 18:18
* detail:头部认证
*/
public class HeaderOkhttpClient extends CacheOkhttpClient{
private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public HeaderOkhttpClient(Context mContext)
{
super(mContext);
}
@Override
protected OkHttpClient.Builder richBuild(OkHttpClient.Builder builder) {
builder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
// key 键值对 决定需求的添加
Request.Builder requestBuilder = request.newBuilder()
.addHeader("timestamp",dateFormat.format(new Date()))
.addHeader("encrypt", MD5Util.getMD5(dateFormat.format(new Date())+""));// 根据自己需求修改头部的key,这边只是一种时间戳和某个字符进行md5加密简单认证。
Log.e(HttpConstant.TAG,"服务器返回数据:"+chain.proceed(requestBuilder.build()).body().string());
return chain.proceed(requestBuilder.build());
}
});
return super.richBuild(builder);
}
}
5、服务器返回数据处理
假如服务器返回参数和字段如下:
{
"errCode": 0,
"res":"success!",
"data":{
"name":"张三",
"age":3,
"phone":"157xxxxxx"
}
}
根据服务器返回报文一般都是统一格式的,为了方便把返回的参数进行分装成Result< T >,其中T泛型就是data对应的字段。详细代码如下:
/**
* author:hjy
* date:2017/8/29 10:33
* detail:实体类基类
*/
public class Result implements Serializable {
// 服务器回调码 0 成功 1 失败
private int errCode;
// 服务器提示语
private String res;
// json数据
private T data;
public int getErrCode() {
return errCode;
}
public void setErrCode(int errCode) {
this.errCode = errCode;
}
public String getRes() {
return res;
}
public void setRes(String res) {
this.res = res;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
定义完服务器数据格式后,定义RxDataHelper类对Observable整体的变换,换句话说对返回数据做转换处理。具体如下:
public class RxDataHelper {
public static Observable.Transformer,T> handResult()
{
return new Observable.Transformer, T>() {
@Override
public Observable call(Observable> resultObservable) {
return resultObservable.flatMap(new Func1, Observable>() {
@Override
public Observable call(Result tResult) {
if (tResult == null)
return Observable.error(new HttpException(HttpErrorCode.CODE_DATA_ERROR, HttpErrorMsg.DATA_ERROR));
LogUtil.e(HttpConstant.TAG, "解析完服务器数据:" + tResult);
// 成功数据
if (tResult.getErrCode() == 0)
return Observable.just(tResult.getData());
// 数据失败
else if (tResult.getErrCode() == 1)
return Observable.error(new HttpException(tResult.getErrCode(),
tResult.getRes()));
return Observable.empty();
}
});
}
};
}
}
假设tResult.getErrCode() == 0是代表服务器返回成功数据,tResult.getErrCode() == 1是代表服务器返回失败数据。服务器返回成功后,使用just(T…): 将传入的参数依次发送出来。服务器返回失败,对失败数据进一步处理。
服务器返回成功后,数据进一步处理。自定义RxSubscriber继承Subscriber< T >订阅,重写onCompleted,onError,onNext这三方法。
public abstract class RxSubscriber extends Subscriber {
private Context mContext;
public RxSubscriber(Context context) {
this.mContext = context;
}
@Override
public void onCompleted() {
LogUtil.e(HttpConstant.TAG, "=========================服务器请求结束(onCompleted)==============================");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Log.e(HttpConstant.TAG,"e:" + e);
String exceptionStr = HttpErrorMsg.UKNOW;
int errorCode = HttpErrorCode.CODE_UNKNOW;
// 自定义异常
if (e instanceof HttpException) {
HttpException eStr = (HttpException) e;
errorCode = eStr.getmErrorCode();
exceptionStr = eStr.getmMessage();
}
// 解析异常
else if (e instanceof JSONException
|| e instanceof ParseException) {
exceptionStr = HttpErrorMsg.DATA_ERROR;
errorCode = HttpErrorCode.CODE_DATA_ERROR;
}
// 连接超时、失败
else if (e instanceof ConnectException ||
e instanceof SocketTimeoutException){
exceptionStr = HttpErrorMsg.TIME_OUT;
errorCode = HttpErrorCode.CODE_TIME_OUT;
}
Toast.makeText(mContext,exceptionStr,Toast.LENGTH_SHORT).show();
LogUtil.e(HttpConstant.TAG,"errorCode:"+errorCode+" mess:"+exceptionStr);
LogUtil.e(HttpConstant.TAG,"=========================服务器请求结束(onError)==============================");
onError(exceptionStr);
}
@Override
public void onNext(T t) {
onSuccess(t);
}
@Override
public void onStart() {
LogUtil.e(HttpConstant.TAG,"========================服务器请求开始(onStart)===============================");
if (!NetUtil.hashNetWork(mContext)) {
Toast.makeText(mContext,"请检查您的网络!",Toast.LENGTH_SHORT).show();
LogUtil.e(HttpConstant.TAG,"请检查您的网络!");
return;
}
}
protected abstract void onSuccess(T t);
protected abstract void onError(String mess);
}
对onError异常进一步处理,自定义HttpException对象,包含mErrorCode错误码、mMessage提示异常提示、mCause异常。
HttpException.java
public class HttpException extends Exception {
private int mErrorCode;
private String mMessage;
private Throwable mCause;
public HttpException(int errorCode, String message) {
super(message);
this.mMessage = message;
this.mErrorCode = errorCode;
}
public HttpException(int errorCode, String message, Throwable cause) {
super(message, cause);
this.mMessage = message;
this.mErrorCode = errorCode;
this.mCause = cause;
}
public int getmErrorCode() {
return mErrorCode;
}
public void setmErrorCode(int mErrorCode) {
this.mErrorCode = mErrorCode;
}
public String getmMessage() {
return mMessage;
}
public void setmMessage(String mMessage) {
this.mMessage = mMessage;
}
public Throwable getmCause() {
return mCause;
}
public void setmCause(Throwable mCause) {
this.mCause = mCause;
}
}
6、线程控制Scheduler
Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。
Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。
Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。
Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O 操作的等待时间会浪费 CPU。
另外, Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。
创建一个线程调度工具类:
public class RxSchedulersHelper {
public static Observable.Transformer doIoAndMain() {
return new Observable.Transformer() {
@Override
public Observable call(Observable tObservable) {
return tObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
具体代码查看:
GitHub项目地址:https://github.com/hjy15759633702/TestHttp