Okhttp3+Rxjava+Retrofit2封装
来源:互联网 发布:郭敬明作品集软件下载 编辑:程序博客网 时间:2024/05/19 16:22
一、简介
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<String,String> params = new HashMap<>(); params.put("account","test"); params.put("password","test"); Observable<Result<User>> observable = HttpManager.init().with(MainActivity.this).login(params); observable.compose(RxSchedulersHelper.<Result<User>>doIoAndMain()) .compose(RxDataHelper.<User>handResult(LifeCycleEvent.DESTROY,lifecycleSubject)) .subscribe(new RxSubscriber<User>(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<T> 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 <T> Observable.Transformer<Result<T>,T> handResult() { return new Observable.Transformer<Result<T>, T>() { @Override public Observable<T> call(Observable<Result<T>> resultObservable) { return resultObservable.flatMap(new Func1<Result<T>, Observable<T>>() { @Override public Observable<T> call(Result<T> 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<T> extends Subscriber<T> { 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 <T> Observable.Transformer<T,T> doIoAndMain() { return new Observable.Transformer<T,T>() { @Override public Observable<T> call(Observable<T> tObservable) { return tObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; }}
具体代码查看:
GitHub项目地址:https://github.com/hjy15759633702/TestHttp
- Okhttp3+Rxjava+Retrofit2封装
- retrofit2+okhttp3+rxjava网络封装
- Rxjava+Retrofit2+Okhttp3
- 初探Okhttp3+Retrofit2+RXJava
- retrofit2.0+okhttp3+rxjava的封装(简单好用)
- Rxjava2+okhttp3+Retrofit2封装
- Retrofit2初尝试(rxjava + okhttp3)
- Retrofit2+OkHttp3+RxJava搭建网络框架
- Retrofit2+OkHttp3+RxJava搭建网络框架
- Android Retrofit2+OkHttp3+RxJava 三联合
- Retrofit2+OkHttp3+RxJava构建网络框架
- Retrofit2+Rxjava学习到封装
- retrofit2+okhttp3+ rxjava 遇到的问题及解决方案
- Retrofit2-RxJava-Dagger2-MVP完美封装
- Retrofit2.0通俗易懂的学习姿势,Retrofit2.0 + OkHttp3 + Gson + RxJava
- Retrofit2.0通俗易懂的学习姿势,Retrofit2.0 + OkHttp3 + Gson + RxJava
- Retrofit2.0通俗易懂的学习姿势,Retrofit2.0 + OkHttp3 + Gson + RxJava
- retrofit2.0通俗易懂的学习姿势,Retrofit2.0 + OkHttp3 + Gson + RxJava
- Android事件触摸机制
- Python基础-高级特性-切片(Slice)
- Windows程序设计:画刷
- 快速求素数表——埃氏筛法与欧拉筛法
- Ruby 加密(md5,sha1,base64)
- Okhttp3+Rxjava+Retrofit2封装
- Redis入门
- idea搭建ssm项目
- BigDecimal类型的使用
- 存储过程的优缺点
- linux kthread
- 通过Java代码对咳嗽症状的一种简单表示(人工智能)
- 程序人生,从这里起航!——我的第一篇CSDN博客
- Linux 用户和用户组管理