学如逆水行舟,逆水行舟。

去年的时候学习了Rxjava和Retrofit的根本用法,但始终没有在理论我的项目中使用。往年开做新我的项目,果决在新我的项目中引入了RxJava和Retrofit。本篇文章将介绍笔者在我的项目中对Retrofit的封装。
先来看一下封装过后的Retrofit如何应用。

RetrofitHelper.getApiService()                .getMezi()                .compose(this.<List<MeiZi>>bindToLifecycle())                .compose(ProgressUtils.<List<MeiZi>>applyProgressBar(this))                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new DefaultObserver<List<MeiZi>>() {                    @Override                    public void onSuccess(List<MeiZi> response) {                        showToast("申请胜利,妹子个数为" + response.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'}

上面将通过几个大节对本次封装作具体的解析:

  • 服务器响应数据的基类BasicResponse
  • 构建初始化Retrofit的工具类IdeaApi
  • 通过GsonConverterFactory获取实在响应数据
  • 封装DefaultObserver解决服务器响应
  • 解决加载Loading
  • 治理Retrofit生命周期
  • 如何应用封装
  • 小结

一.服务器响应数据的基类BasicResponse。

假设服务器返回的Json数据格式如下:

{ "code": 200, "message": "胜利", "content": {    ...    }}

依据Json数据格式构建咱们的BasicResponse(BasicResponse中的字段内容须要依据本人服务器返回的数据确定)。代码如下:

public class BasicResponse<T> {    private int code;    private String message;    private T content;    ...此处省去get、set办法。

二.构建初始化Retrofit的工具类IdeaApi。

该类通过RetrofitUtils来获取ApiService的实例。代码如下:

public class IdeaApi {    public static <T> T getApiService(Class<T> cls,String baseUrl) {        Retrofit retrofit = RetrofitUtils .getRetrofitBuilder(baseUrl).build();        return retrofit.create(cls);    }}

RetrofitUtils用来构建Retrofit.Builder,并对OkHttp做以下几个方面的配置:

  1. 设置日志拦截器,拦挡服务器返回的json数据。Retrofit将申请到json数据间接转换成了实体类,但有时候咱们须要查看json数据,Retrofit并没有提供间接获取json数据的性能。因而咱们须要自定义一个日志拦截器拦挡json数据,并输出到控制台。
  2. 设置Http申请头。给OkHttp 增加申请头拦截器,配置申请头信息。还能够为接口对立增加申请头数据。例如,把用户名、明码(或者token)对立增加到申请头。后续每个接口的申请头中都会携带用户名、明码(或者token)数据,防止了为每个接口独自增加。
  3. 为OkHttp配置缓存。同样能够同过拦截器实现缓存解决。包含管制缓存的最大生命值,管制缓存的过期工夫。
  4. 如果采纳https,咱们还能够在此解决证书校验以及服务器校验。
  5. 为Retrofit增加GsonConverterFactory。此处是一个比拟重要的环节,将在后边具体解说。

RetrofitUtils 代码如下:

public class RetrofitUtils {    public static OkHttpClient.Builder getOkHttpClientBuilder() {        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {            @Override            public void log(String message) {                try {                    LogUtils.e("OKHttp-----", URLDecoder.decode(message, "utf-8"));                } catch (UnsupportedEncodingException e) {                    e.printStackTrace();                    LogUtils.e("OKHttp-----", message);                }            }        });        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);        File cacheFile = new File(Utils.getContext().getCacheDir(), "cache");        Cache cache = new Cache(cacheFile, 1024 * 1024 * 100); //100Mb        return new OkHttpClient.Builder()                .readTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)                .connectTimeout(Constants.DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS)                .addInterceptor(loggingInterceptor)                .addInterceptor(new HttpHeaderInterceptor())                .addNetworkInterceptor(new HttpCacheInterceptor())               // .sslSocketFactory(SslContextFactory.getSSLSocketFactoryForTwoWay())  // https认证 如果要应用https且为自定义证书 能够去掉这两行正文,并自行配制证书。               // .hostnameVerifier(new SafeHostnameVerifier())                .cache(cache);    }    public static Retrofit.Builder getRetrofitBuilder(String baseUrl) {        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();        OkHttpClient okHttpClient = RetrofitUtils.getOkHttpClientBuilder().build();        return new Retrofit.Builder()                .client(okHttpClient)                .addConverterFactory(GsonConverterFactory.create(gson))                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())                .baseUrl(baseUrl);    }}

三.通过GsonConverterFactory获取实在响应数据

在第一节中咱们构建了服务器响应数据BasicResponse,BasicResponse由code、message、和content三个字段。其中code为服务器返回的错误码。咱们会当时和服务器约定胜利时的code值,比方200示意申请胜利。但通常在申请服务器数据过程中免不了会呈现各种谬误。例如用户登录时明码谬误、申请参数谬误的状况。此时服务器会依据谬误状况返回对应的错误码。一般来说,咱们只关怀胜利时即code为200时的content数据。而对于code不为200时咱们只须要给出对应的Toast提醒即可。事实上咱们对咱们有用的仅仅时code为200时的content数据。因而咱们能够思考过滤掉code和message,在申请胜利的回调中只返回content的内容。

在此种状况下就须要咱们通过自定义GsonConverterFactory来实现了。咱们能够间接从Retrofit的源码中copy出GsonConverterFactory的三个相干类来做批改。

其中最终要的一部分是批改GsonResponseBodyConverter中的convert办法。在该办法中拿到服务器响应数据并判断code是否为200。如果是,则获取到content并返回,如果不是,则在此处能够抛出对应的自定义的异样。而后再Observer中对立解决异常情况。GsonResponseBodyConverter代码如下:

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, Object> {    private final TypeAdapter<T> adapter;    GsonResponseBodyConverter(TypeAdapter<T> adapter) {        this.adapter = adapter;    }    @Override    public Object convert(ResponseBody value) throws IOException {        try {            BasicResponse response = (BasicResponse) adapter.fromJson(value.charStream());            if (response.getCode()==200) {            return response.getResults();            } else {                // 特定 API 的谬误,在相应的 DefaultObserver 的 onError 的办法中进行解决                throw new ServerResponseException(response.getCode(), response.getMessage());            }        } finally {            value.close();        }        return null;    }}

四.构建DefaultObserver解决服务器响应。

上一节中咱们讲到了在申请服务器时可能呈现的一些例如明码谬误、参数谬误的状况,服务器给咱们返回了对应的错误码,咱们依据错误码抛出了对应自定义异样。除此之外在咱们发动网络申请时还可能产生一些异常情况。例如没有网络、申请超时或者服务器返回了数据但在解析时呈现了数据解析异样等。对于这样的状况咱们也要进行对立解决的。那么咱们就须要自定义一个DefaultObserver类继承Observer,并重写相应的办法。
该类中最重要的两个办法时onNext和onError。

1.在服务器返回数据胜利的状况下会回调到onNext办法。因而咱们能够在DefaultObserver中定义一个形象办法onSuccess(T response),在调用网络时重写onSuccess办法即可。

2.如果在申请服务器过程中呈现任何异样,都会回调到onError办法中。包含上节中咱们本人抛出的异样都会回调到onError。因而咱们的重头戏就是解决onError。在onError中咱们依据异样信息给出对应的Toast提醒即可。

DefaultObserver类的代码如下:

public abstract class DefaultObserver<T> implements Observer<T> {    @Override    public void onSubscribe(Disposable d) {    }    @Override    public void onNext(T response) {        onSuccess(response);        onFinish();    }    @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(ExceptionReason.CONNECT_ERROR);        } else if (e instanceof InterruptedIOException) {   //  连贯超时            onException(ExceptionReason.CONNECT_TIMEOUT);        } else if (e instanceof JsonParseException                || e instanceof JSONException                || e instanceof ParseException) {   //  解析谬误            onException(ExceptionReason.PARSE_ERROR);        }else if(e instanceof ServerResponseException){            onFail(e.getMessage());        } else {            onException(ExceptionReason.UNKNOWN_ERROR);        }        onFinish();    }    @Override    public void onComplete() {    }    /**     * 申请胜利     *     * @param response 服务器返回的数据     */    abstract public void onSuccess(T response);    /**     * 服务器返回数据,但响应码不为200     *     */    public void onFail(String message) {        ToastUtils.show(message);    }    public void onFinish(){}    /**     * 申请异样     *     * @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,    }}

五.解决加载Loading

对于Loading咱们能够通过RxJava的compose操作符来做一个十分优雅的解决。首先定义一个ProgressUtils工具类,而后通过RxJava的ObservableTransformer做一个变换来解决Loading。想要显示Loading,只须要加上.compose(ProgressUtils.< T >applyProgressBar(this))即可。
ProgressUtils代码如下:

public class ProgressUtils {    public static <T> ObservableTransformer<T, T> applyProgressBar(            @NonNull final Activity activity, String msg) {        final WeakReference<Activity> activityWeakReference = new WeakReference<>(activity);        final DialogUtils dialogUtils = new DialogUtils();        dialogUtils.showProgress(activityWeakReference.get());        return new ObservableTransformer<T, T>() {            @Override            public ObservableSource<T> apply(Observable<T> upstream) {                return upstream.doOnSubscribe(new Consumer<Disposable>() {                    @Override                    public void accept(Disposable disposable) throws Exception {                    }                }).doOnTerminate(new Action() {                    @Override                    public void run() throws Exception {                        Activity context;                        if ((context = activityWeakReference.get()) != null                                && !context.isFinishing()) {                            dialogUtils.dismissProgress();                        }                    }                }).doOnSubscribe(new Consumer<Disposable>() {                    @Override                    public void accept(Disposable disposable) throws Exception {                        /*Activity context;                        if ((context = activityWeakReference.get()) != null                                && !context.isFinishing()) {                            dialogUtils.dismissProgress();                        }*/                    }                });            }        };    }    public static <T> ObservableTransformer<T, T> applyProgressBar(            @NonNull final Activity activity) {        return applyProgressBar(activity, "");    }}

至此对于RxJava和Retrofit的二次封装曾经根本实现。然而咱们不能疏忽了很重要的一点,就是网络申请的生命周期。咱们将在下一节中具体解说。

六、治理Retrofit生命周期

当activity被销毁时,网络申请也应该随之终止的。要不然就可能造成内存透露。会重大影到响App的性能!因而Retrofit生命周期的治理也是比拟重要的一点内容。在这里咱们应用 RxLifecycle来对Retrofit进行生命周期治理。其应用流程如下:

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(留神应用继承V4包下的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();    }}

3.应用compose操作符治理Retrofit生命周期了:

myObservable            .compose(bindToLifecycle())            .subscribe();或者myObservable    .compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))    .subscribe();

对于RxLifecycle的具体应用办法能够参考 RxLifecycle官网

七.如何应用封装

后面几节内容解说了如何RxJava进行二次封装,封装局部的代码能够放在咱们我的项目的Library模块中。那么封装好之后咱们应该如何在app模块中应用呢?

1.定义一个接口来寄存咱们我的项目的API

public interface IdeaApiService {    /**     * 此接口服务器响应数据BasicResponse的泛型T应该是List<MeiZi>     * 即BasicResponse<List<MeiZi>>     * @return BasicResponse<List<MeiZi>>     */    @Headers("Cache-Control: public, max-age=10")//设置缓存 缓存工夫为100s    @GET("福利/10/1")    Observable<List<MeiZi>> getMezi();    /**     * 登录 接口为假接口 并不能返回数据     * @return     */    @POST("login.do")    Observable<LoginResponse> login(@Body LoginRequest request);    /**     * 刷新token 接口为假接口 并不能返回数据     * @return     */    @POST("refresh_token.do")    Observable<RefreshTokenResponseBean> refreshToken(@Body RefreshTokenRequest request);    @Multipart    @POST("upload/uploadFile.do")    Observable<BasicResponse> uploadFiles(@Part List<MultipartBody.Part> partList);}

2.定义一个RetrofitHelper 类,通过IdeaApi来获取IdeaApiService的实例。

public class RetrofitHelper {    private static IdeaApiService mIdeaApiService;    public static IdeaApiService getApiService(){        return mIdeaApiService;    }    static {       mIdeaApiService= IdeaApi.getApiService(IdeaApiService.class, Constants.API_SERVER_URL);    }}

3.在Activity或者Fragment中发动网络申请

    /**     * Get申请     * @param view     */    public void getData(View view) {        RetrofitHelper.getApiService()                .getMezi()                .compose(this.<List<MeiZi>>bindToLifecycle())                .compose(ProgressUtils.<List<MeiZi>>applyProgressBar(this))                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new DefaultObserver<List<MeiZi>>() {                    @Override                    public void onSuccess(List<MeiZi> response) {                        showToast("申请胜利,妹子个数为" + response.size());                    }                });    }

八.小结

本篇文章次要解说了Rxjava和Retrofit的二次封装。以上内容也是笔者参考多方面的材料通过长时间的改变优化而来。但鉴于自己能力无限,其中也防止不了呈现不当之处。还请大家多多包涵。另外,在投稿郭神公众号时文章可能还存在很多解决不优雅的中央,比方对响应数据的解决以及对Loading的解决。在投稿被推送后收到了很多小伙伴的倡议,因而笔者也参考了大家的意见并做了优化,在此感激大家。最初如果有疑难欢送在文章留言评论。