最流行的网络请求框架Rxjava2+Retrofit完美封装

来源:互联网 发布:手机淘宝2017历史版本 编辑:程序博客网 时间:2024/05/16 12:48

(一)最流行的网络请求框架Rxjava2+Retrofit完美封装

原创 2017年04月30日 03:29:43
  • 编辑
  • 删除

* 本文同步发表在简书,转载请注明出处。

* 本篇文章已授权微信公众号 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。在该类中主要完成三个功能,即:

  1. 设置日志拦截器拦截服务器返回的json数据。Retrofit将请求到json数据直接转换成了实体类,但有时候我们需要查看json数据,Retrofit并没有提供直接获取json数据的功能。因此我们需要自定义一个日志拦截器拦截json数据,并输入到控制台。
  2. 设置 Http 拦截器,处理缓存问题。通过拦截器拦截Http请求头,为请求头配置缓存信息,包括控制缓存的最大生命值,控制缓存的过期时间。
  3. 获取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

源码传送门

阅读全文
0 0