最流行的网络请求框架Rxjava2+Retrofit完美封装
来源:互联网 发布:手机淘宝2017历史版本 编辑:程序博客网 时间:2024/05/16 12:48
(一)最流行的网络请求框架Rxjava2+Retrofit完美封装
* 本文同步发表在简书,转载请注明出处。
* 本篇文章已授权微信公众号 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()); } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
没错,就是这么简洁的一个链式调用,可以显示加载动画,还加入了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"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
为了方便依赖库版本的修改我们采用”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'}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
下面将通过几个小节对本次封装作详细的解析:
- 构建初始化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(); } } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
二.构建服务器响应数据的基类BasicResponse。假定服务器返回的Json数据格式如下:
{ "code": 200, "message": "成功", "content": { ... }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
根据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; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
三.构建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); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
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; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
四.处理加载动画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(); } }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
加入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, }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
五、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'
- 1
- 2
- 3
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);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
我们项目的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(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
接下来我们在使用Rxjava时就可以通过以下代码去管理生命周期了:
myObservable .compose(bindToLifecycle()) .subscribe();或者myObservable .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY)) .subscribe();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
关于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();}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
源码传送门
- 最流行的网络请求框架Rxjava2+Retrofit完美封装
- (一)最流行的网络请求框架Rxjava2+Retrofit完美封装
- (二)最流行的网络请求框架Rxjava2+Retrofit之Token处理
- (三)最流行的网络请求框架Rxjava2+Retrofit之文件上传
- Retrofit2+Rxjava2网络请求框架的封装
- RxJava2和Retrofit Http请求的封装
- 简单实现RxJava2+Okhttp+Retrofit2的网络请求框架封装
- RxJava2+retrofit实现网络封装
- rxjava2 与 retrofit 网络请求
- 封装Retrofit + okhttp + rxjava网络请求框架
- RxJava2+Retrofit2网络请求框架封装及使用
- Retrofit+Rxjava 网络请求的完美封装(二)(观察者模式)
- retrofit网络请求的简单封装使用
- rxjava+retrofit+okhttp实现流行的网络请求
- retrofit网络请求框架
- 网络请求框架:Retrofit
- 网络请求框架Retrofit
- Retrofit网络请求框架
- kettle 使用java脚本连接获取数据库中的值
- 软件开发测试中七个无用的指标,看完别再受误导了
- WALTR2(ios数据传输工具)官方破解版V2.6.6下载 | 无需waltr2 激活码 | waltr2 windows
- pycharm突然没有代码提示了
- 729. My Calendar I
- 最流行的网络请求框架Rxjava2+Retrofit完美封装
- Struts ValueStack解析
- 用jmeter测试基于mina的tcp服务时,发生阻塞,发现将eol改为10不阻塞。
- 大学生创业你经历过吗?
- 高通平台 linux kernel pinctrl介绍
- STM32F4 的DAC 配置
- CRM 2016解读
- 【Scikit-Learn 中文文档】高斯混合模型
- 计算哈希值工具简介