(一)最流行的网络请求框架Rxjava2+Retrofit完美封装
来源:互联网 发布:同在一起网络剧 编辑:程序博客网 时间:2024/05/21 15:46
* 本文同步发表在简书,转载请注明出处。
* 本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
要说2016年最火的Android技术是什么,毫无疑问肯定是RxJava+Retrofit+Mvp。现如今2017年也已经过了快一半了。相信做android开发的小伙伴对RxJava和Retrofit也不再陌生。即使没有刻意的去学习过,也应该对RxJava和Retrofit有个一知半解。去年的时候学习了Rxjava和Retrofit的基本用法,但一直没有在实际项目中运用。今年开做新项目,果断在新项目中引入了RxJava和Retrofit。本篇文章将介绍笔者在项目中对Retrofit的封装。
先来看一下封装过后的Retrofit如何使用。
IdeaApi.getApiService() .getMezi() .compose(this.<BasicResponse<List<MeiZi>>>bindToLifecycle()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new DefaultObserver<BasicResponse<List<MeiZi>>>(this) { @Override public void onSuccess(BasicResponse<List<MeiZi>> response) { List<MeiZi> results = response.getResults(); showToast("请求成功,妹子个数为"+results.size()); } });
没错,就是这么简洁的一个链式调用,可以显示加载动画,还加入了Retrofit生命周期的管理。
开始之前需要先在module项目里的Gradle文件中添加用到的依赖库
compile "io.reactivex.rxjava2:rxjava:$rootProject.ext.rxjava2Version" compile "com.squareup.retrofit2:retrofit:$rootProject.ext.retrofit2Version" compile "com.squareup.retrofit2:converter-scalars:$rootProject.ext.retrofit2Version" compile "com.squareup.retrofit2:converter-gson:$rootProject.ext.retrofit2Version" compile "com.squareup.retrofit2:adapter-rxjava2:$rootProject.ext.retrofit2Version" compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' compile 'com.squareup.okhttp3:logging-interceptor:3.4.1' compile "com.trello.rxlifecycle2:rxlifecycle:$rootProject.ext.rxlifecycle" //compile "com.trello.rxlifecycle2:rxlifecycle-android:$rootProject.ext.rxlifecycle" compile "com.trello.rxlifecycle2:rxlifecycle-components:$rootProject.ext.rxlifecycle"
为了方便依赖库版本的修改我们采用”io.reactivex.rxjava2:rxjava:$rootProject.ext.rxjava2Version”这中方式添加依赖,因此需要在project的build.gradle文件的加上以下内容:
ext { supportLibVersion = '25.1.0' butterknifeVersion = '8.5.1' rxjava2Version = '2.0.8' retrofit2Version = '2.2.0' rxlifecycle='2.1.0' gsonVersion = '2.8.0'}
下面将通过几个小节对本次封装作详细的解析:
- 构建初始化Retrofit的工具类IdeaApi
- 服务器响应数据的基类BasicResponse
- 封装DefaultObserver处理服务器响应
- 处理加载动画ProgressDialog
- Rxjava生命周期处理
一.构建初始化Retrofit的工具类IdeaApi。在该类中主要完成三个功能,即:
- 设置日志拦截器拦截服务器返回的json数据。Retrofit将请求到json数据直接转换成了实体类,但有时候我们需要查看json数据,Retrofit并没有提供直接获取json数据的功能。因此我们需要自定义一个日志拦截器拦截json数据,并输入到控制台。
- 设置 Http 拦截器,处理缓存问题。通过拦截器拦截Http请求头,为请求头配置缓存信息,包括控制缓存的最大生命值,控制缓存的过期时间。
获取Retrofit实例。通过单利模式获取Retrofit实例。
实现代码如下:
public class IdeaApi { private IdeaApiService service; private IdeaApi() { // HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); // 日志拦截器 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor((message)-> { try { String text = URLDecoder.decode(message, "utf-8"); LogUtils.e("OKHttp-----", text); } catch (UnsupportedEncodingException e) { e.printStackTrace(); LogUtils.e("OKHttp-----", message); } }); interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); File cacheFile = new File(Utils.getContext().getCacheDir(), "cache"); Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb OkHttpClient okHttpClient = new OkHttpClient.Builder() .readTimeout(IdeaApiService.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) .connectTimeout(IdeaApiService.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS) .addInterceptor(interceptor) .addNetworkInterceptor(new HttpCacheInterceptor()) .cache(cache) .build(); Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create(); Retrofit retrofit = new Retrofit.Builder() .client(okHttpClient) .addConverterFactory(GsonConverterFactory.create(gson)) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(IdeaApiService.API_SERVER_URL) .build(); service = retrofit.create(IdeaApiService.class); } // 创建单例 private static class SingletonHolder { private static final IdeaApi INSTANCE = new IdeaApi(); } public static IdeaApiService getApiService() { return SingletonHolder.INSTANCE.service; } class HttpCacheInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); if (!NetworkUtils.isConnected()) { request = request.newBuilder() .cacheControl(CacheControl.FORCE_CACHE) .build(); LogUtils.d("Okhttp", "no network"); } Response originalResponse = chain.proceed(request); if (NetworkUtils.isConnected()) { //有网的时候读接口上的@Headers里的配置,可以在这里进行统一的设置 String cacheControl = request.cacheControl().toString(); return originalResponse.newBuilder() .header("Cache-Control", cacheControl) .removeHeader("Pragma") .build(); } else { return originalResponse.newBuilder() .header("Cache-Control", "public, only-if-cached, max-stale=2419200") .removeHeader("Pragma") .build(); } } }}
二.构建服务器响应数据的基类BasicResponse。假定服务器返回的Json数据格式如下:
{ "code": 200, "message": "成功", "content": { ... }}
根据Json数据格式构建我们的BasicResponse(BasicResponse中的字段内容需要根据自己服务器返回的数据确定)。代码如下:
public class BasicResponse<T> { private int code; private String message; private T content; public T getContent() { return content; } public void setContent(T content) { this.content = content; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; }}
三.构建DefaultObserver处理服务器响应数据。定义DefaultObserver类继承Observer,并重写相应的方法。
在请求数据过程中免不了会出现各种错误或者异常,可以分为两种情况:
1.像登录时密码错误、请求参数错误的情况,即服务器返回了错误的数据,我们可以称之为错误。只要服务器返回数据,Observer中的onNext()方法就会被执行。但有时服务器返回错误数据并不是我们想要的,因此我们需要对错误数据进行处理。我们可以和服务端事先约定正常情况的请求码,如上面Json中的code等于200时视为数据正常,code不为200时视为数据错误。因此我们可以定义出一个请求数据正常的抽象方法onSuccess(),在code为200时调用,并在请求数据的页面重写该方法。另外定义一个请求失败的方法onFail(),在code不为200时调用,并Toast出错误原因。请求网络页面不必重写该方法。代码如下:
@Override public void onNext(T response) { mBaseImpl.dismissProgress(); if (response.getCode() == 200) { onSuccess(response); } else { onFail(response); } } /** * 请求数据成功 且响应码为200 * @param response 服务器返回的数据 */ abstract public void onSuccess(T response); /** * 服务器返回数据,但响应码不为200 * @param response 服务器返回的数据 */ public void onFail(T response) { String message = response.getMessage(); if (TextUtils.isEmpty(message)) { ToastUtils.show(R.string.response_return_error); } else { ToastUtils.show(message); } }
2.请求网络时出现异常情况,如网络连接失败、网络连接超时、数据解析异常等情况。我们可以称之为异常,即这种情况下Observer的onError()方法被调用。因此我们可以定义一个onException()的方法,并根据不同的异常在onException()方法中给出对应的Toast提示。代码如下:
@Override public void onError(Throwable e) { LogUtils.e("Retrofit", e.getMessage()); if (e instanceof HttpException) { // HTTP错误 onException(ExceptionReason.BAD_NETWORK); } else if (e instanceof ConnectException || e instanceof UnknownHostException) { // 连接错误 onException(CONNECT_ERROR); } else if (e instanceof InterruptedIOException) { // 连接超时 onException(CONNECT_TIMEOUT); } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) { // 解析错误 onException(PARSE_ERROR); } else { onException(UNKNOWN_ERROR); } } /** * 请求异常 * @param reason */ public void onException(ExceptionReason reason) { switch (reason) { case CONNECT_ERROR: ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT); break; case CONNECT_TIMEOUT: ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT); break; case BAD_NETWORK: ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT); break; case PARSE_ERROR: ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT); break; case UNKNOWN_ERROR: default: ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT); break; } }
四.处理加载动画ProgressDialog
我们可以自定义ProgressBar,然后封装成DialogUtils进行控制ProgressDialog,
DialogUtils代码如下:
public class CommonDialogUtils { // 加载进度的dialog private CustomProgressDialog mProgressDialog; /** * 显示ProgressDialog */ public void showProgress(Context context, String msg) { /* if (context == null || context.isFinishing()) { return; }*/ if(mProgressDialog==null){ mProgressDialog= new CustomProgressDialog.Builder(context) .setTheme(R.style.ProgressDialogStyle) .setMessage(msg) .build(); } if(mProgressDialog!=null&&!mProgressDialog.isShowing()) { mProgressDialog.show(); } } /** * 显示ProgressDialog */ public void showProgress(Context context) { /*if (activity == null || activity.isFinishing()) { return; }*/ if(mProgressDialog==null){ mProgressDialog= new CustomProgressDialog.Builder(context) .setTheme(R.style.ProgressDialogStyle) .build(); } if(mProgressDialog!=null&&!mProgressDialog.isShowing()) { mProgressDialog.show(); } } /** * 取消ProgressDialog */ public void dismissProgress() { if (mProgressDialog != null&&mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } }}
加入ProgressBar后完整的DefaultObserver
public abstract class DefaultObserver<T extends BasicResponse> implements Observer<T> { private Activity activity; // Activity 是否在执行onStop()时取消订阅 private boolean isAddInStop = false; private CommonDialogUtils dialogUtils; public DefaultObserver(Activity activity) { this.activity = activity; dialogUtils=new CommonDialogUtils(); dialogUtils.showProgress(activity); } public DefaultObserver(Activity activity, boolean isShowLoading) { this.activity = activity; dialogUtils=new CommonDialogUtils(); if (isShowLoading) { dialogUtils.showProgress(activity,"Loading..."); } } @Override public void onSubscribe(Disposable d) { } @Override public void onNext(T response) { dismissProgress(); if (!response.isError()) { onSuccess(response); } else { onFail(response); } /*if (response.getCode() == 200) { onSuccess(response); } else { onFail(response); }*/ } private void dismissProgress(){ if(dialogUtils!=null){ dialogUtils.dismissProgress(); } } @Override public void onError(Throwable e) { LogUtils.e("Retrofit", e.getMessage()); dismissProgress(); if (e instanceof HttpException) { // HTTP错误 onException(ExceptionReason.BAD_NETWORK); } else if (e instanceof ConnectException || e instanceof UnknownHostException) { // 连接错误 onException(CONNECT_ERROR); } else if (e instanceof InterruptedIOException) { // 连接超时 onException(CONNECT_TIMEOUT); } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) { // 解析错误 onException(PARSE_ERROR); } else { onException(UNKNOWN_ERROR); } } @Override public void onComplete() { } /** * 请求成功 * * @param response 服务器返回的数据 */ abstract public void onSuccess(T response); /** * 服务器返回数据,但响应码不为200 * * @param response 服务器返回的数据 */ public void onFail(T response) { String message = response.getMessage(); if (TextUtils.isEmpty(message)) { ToastUtils.show(R.string.response_return_error); } else { ToastUtils.show(message); } } /** * 请求异常 * * @param reason */ public void onException(ExceptionReason reason) { switch (reason) { case CONNECT_ERROR: ToastUtils.show(R.string.connect_error, Toast.LENGTH_SHORT); break; case CONNECT_TIMEOUT: ToastUtils.show(R.string.connect_timeout, Toast.LENGTH_SHORT); break; case BAD_NETWORK: ToastUtils.show(R.string.bad_network, Toast.LENGTH_SHORT); break; case PARSE_ERROR: ToastUtils.show(R.string.parse_error, Toast.LENGTH_SHORT); break; case UNKNOWN_ERROR: default: ToastUtils.show(R.string.unknown_error, Toast.LENGTH_SHORT); break; } } /** * 请求网络失败原因 */ public enum ExceptionReason { /** * 解析数据失败 */ PARSE_ERROR, /** * 网络问题 */ BAD_NETWORK, /** * 连接错误 */ CONNECT_ERROR, /** * 连接超时 */ CONNECT_TIMEOUT, /** * 未知错误 */ UNKNOWN_ERROR, }}
五、Rxjava生命周期处理
在Activity或者Fragment中使用RxJava时我们有必要对RxJava的生命周期进行管理,否则可能引起内存泄漏问题。在这里我们使用 RxLifecycle来对RxJava进行生命周期管理。
1.在gradel中添加依赖如下:
compile 'com.trello.rxlifecycle2:rxlifecycle:2.1.0'compile 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'
2.让我们的BaseActivity继承RxAppCompatActivity。具体代码如下:
public abstract class BaseActivity extends RxAppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); init(savedInstanceState); } protected void showToast(String msg) { ToastUtils.show(msg); } protected abstract @LayoutRes int getLayoutId(); protected abstract void init(Bundle savedInstanceState);}
我们项目的BaseFragment继承RxFragment,如下:
public abstract class BaseFragment extends RxFragment { public View rootView; public LayoutInflater inflater; @Nullable @Override public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); this.inflater = inflater; if (rootView == null) { rootView = inflater.inflate(this.getLayoutId(), container, false); init(savedInstanceState); } ViewGroup parent = (ViewGroup) rootView.getParent(); if (parent != null) { parent.removeView(rootView); } return rootView; } protected abstract int getLayoutId(); protected abstract void init(Bundle savedInstanceState); protected void showToast(String msg) { ToastUtils.show(msg); } @Override public void onResume() { super.onResume(); } @Override public void onPause() { super.onPause(); } @Override public void onDestroyView() { super.onDestroyView(); }}
接下来我们在使用Rxjava时就可以通过以下代码去管理生命周期了:
myObservable .compose(bindToLifecycle()) .subscribe();或者myObservable .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) .subscribe();
关于RxLifecycle的详细使用方法可以参考 RxLifecycle官网
最后构建存放网络请求的接口IdeaApiService,项目中所有的请求接口都可以放到这里,具体代码如下:
public interface IdeaApiService { /** * 网络请求超时时间毫秒 */ int DEFAULT_TIMEOUT = 15000; String HOST = "http://gank.io/"; String API_SERVER_URL = HOST + "api/data/"; @Headers("Cache-Control: public, max-age=86400") // 设置缓存 @GET("福利/10/1") Observable<BasicResponse<List<MeiZi>>> getMezi();}
源码传送门
- (一)最流行的网络请求框架Rxjava2+Retrofit完美封装
- 最流行的网络请求框架Rxjava2+Retrofit完美封装
- (二)最流行的网络请求框架Rxjava2+Retrofit之Token处理
- (三)最流行的网络请求框架Rxjava2+Retrofit之文件上传
- Retrofit2+Rxjava2网络请求框架的封装
- RxJava2和Retrofit Http请求的封装
- 简单实现RxJava2+Okhttp+Retrofit2的网络请求框架封装
- RxJava2+retrofit实现网络封装
- Retrofit+Rxjava 网络请求的完美封装(二)(观察者模式)
- rxjava2 与 retrofit 网络请求
- (安卓) MVP 框架 (Rxjava2+Retrofit)结合 实现网络请求
- 封装Retrofit + okhttp + rxjava网络请求框架
- RxJava2+Retrofit2网络请求框架封装及使用
- Retrofit+Rxjava 网络请求的简单封装(一)(观察者模式)
- 一步步搭建Retrofit+RxJava+MVP网络请求框架(一)
- 一步步搭建Retrofit+RxJava+MVP网络请求框架(一)
- Android网络请求框架之Retrofit(一)
- RxJava与Retrofit封装简单的网络请求(二)
- 【Unity】【Code】通用代码库(一)——物体的wasd移动
- 用 Python 统计高频字数
- 算法课第十周作业 | Minimum Path Sum
- Unity3D学习——粒子光环
- 递归的函数(SDUT2176)
- (一)最流行的网络请求框架Rxjava2+Retrofit完美封装
- java多线程学习
- 浅析Hadoop启动脚本
- 如何解决MySQL Visual Studio Cannot create a design window for the selected object
- [Vue.js启航]——数据的双向绑定
- VS如何连接Mysql
- 解决重复提交表单
- itchat4j -- 用Java扩展个人微信号的能力
- python3 if elif else 分支语句