76

Android 之路 (3) - 对Retrofit的封装

 5 years ago
source link: http://fullscreendeveloper.cn/articles/2018/10/10/1539167974165.html?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

引言

上篇中实现了MVP的改造,但是我们的网络请求部分任然是比较冗余了,本篇将对Retrofit进行封装。

正文

思路

对Retrofit进行封装算是我们基础框架的一部分,所以我们需要将这部分代码封装到 library-core 中,对于封装的实现思路如下:

  1. 将Retrofit的创建更改为单例模式创建,增加可配置性,减少重复创建
  2. 将Service的创建也更改为单例模式

创建NetConfiger接口

既然是作为基础框架的一部分,那么在设计上面就需要高自由可配置,所以我们创建一个 NetConfiger 接口,用来进行 baseUrl、拦截器、超时时间、debug等的配置、然后在 Application 中进行配置,具体看以下代码:

**
 * 网络配置器
 * <p>在Application中使用</p>
 */
public interface NetConfiger {
    /**
     * 通用请求地址
     *
     * @return
     */
    String configBaseUrl();

    /**
     * 拦截器
     *
     * @return
     */
    Interceptor[] configInterceptors();

    /**
     * 连接超时时间
     *
     * @return
     */
    long configConnectTimeoutMills();

    /**
     * 读取超时时间
     *
     * @return
     */
    long configReadTimeoutMills();


    /**
     * 是否调试模式
     *
     * @return
     */
    boolean configLogEnable();
}

创建BaseApi

创建BaseApi类,主要用来做Retrofit的生成和OkhttpClient的配置等,先看代码:

/**
 * 基类  API
 */
public class BaseApi {
    /**
     * 网络配置项
     */
    private static NetConfig mConfig = null;
    /**
     * mRetrofit
     */
    private Retrofit mRetrofit;
    /**
     * mClient
     */
    private OkHttpClient mClient;
    /**
     * 默认连接超时时间
     */
    private static final long DEFAULT_CONNECT_TIMEOUT_MILLS = 40 * 1000L;
    /**
     * 默认读取超时时间
     */
    private static final long DEFAULT_READ_TIMEOUT_MILLS = 40 * 1000L;
    /**
     * 实例
     **/
    private static BaseApi instance;

    private BaseApi() {
        mRetrofit = null;
        mClient = null;
    }


    /**
     * 创建 Retrofit
     *
     * @return Retrofit
     */
    public static synchronized Retrofit createRetrofit() {
        return getInstance().getRetrofit();
    }


    /**
     * 单例 获取
     *
     * @return BaseApi
     */
    private static synchronized BaseApi getInstance() {
        if (instance == null)
            instance = new BaseApi();
        return instance;
    }


    /**
     * 创建class
     *
     * @param service 服务class
     * @param <C>     类泛型
     * @return 泛型
     */
    public static <C> C get(Class<C> service) {
        return getInstance().getRetrofit().create(service);
    }


   /**
     * 注册配置
     *
     * @param config
     */
    public static void registerConfig(NetConfig config) {
        BaseApi.mConfig = config;
        //赋值为空
        instance = null;
    }


    /**
     * 获取Retrofit
     *
     * @return 获取Retrofit
     */
    public Retrofit getRetrofit() {
        if (mRetrofit == null) {
            Retrofit.Builder builder = new Retrofit.Builder()
                    .baseUrl(mConfig.configBaseUrl())//配置BaseUrl
                    .client(getHttpClient())// 设置client
                    .addConverterFactory(GsonConverterFactory.create());//gson转换器
            builder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());
            mRetrofit = builder.build();
        }
        return mRetrofit;
    }


    /**
     * 获取httpclient
     *
     * @return OkHttpClient
     */
    private OkHttpClient getHttpClient() {
        if (mClient == null) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder();

            // 连接超时时间
            builder.connectTimeout(mConfig.configConnectTimeoutMills() != 0
                    ? mConfig.configConnectTimeoutMills()
                    : DEFAULT_CONNECT_TIMEOUT_MILLS, TimeUnit.MILLISECONDS);
            // 读取超时时间
            builder.readTimeout(mConfig.configReadTimeoutMills() != 0
                    ? mConfig.configReadTimeoutMills() : DEFAULT_READ_TIMEOUT_MILLS, TimeUnit.MILLISECONDS);

            // 拦截器
            Interceptor[] interceptors = mConfig.configInterceptors();
            if (interceptors != null && interceptors.length > 0) {
                for (Interceptor interceptor : interceptors) {
                    builder.addInterceptor(interceptor);
                }
            }
            if (mConfig.configLogEnable()) {//配置打印
                HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor();
                logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
                builder.addInterceptor(logInterceptor);
            }

            mClient = builder.build();
        }
        return mClient;
    }
}

具体方法说明如下:

registerConfig(NetConfig config)

进行网络的相关配置,在Application中进行调用

getHttpClient()

获取和配置OkHttpClient

连接超时时间

读取超时时间

自定义拦截器

请求日志拦截器

getRetrofit()

获取Retrofit实例

配置BaseUrl

配置client

配置转换器

其他略….

注册相关配置

新建Application类在 AndroidManifest 中引用,并调用BaseApi的registerConfig,传入registerConfig的实例进行相关配置,具体代码如下:

public class DevApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        BaseApi.registerConfig(new NetConfig() {
            @Override
            public String configBaseUrl() {
                return "http://olrt5mymy.bkt.clouddn.com/";
            }

            @Override
            public Interceptor[] configInterceptors() {
                return new Interceptor[0];
            }

            @Override
            public long configConnectTimeoutMills() {
                return 45 * 1000;
            }

            @Override
            public long configReadTimeoutMills() {
                return 45 * 1000;
            }

            @Override
            public boolean configLogEnable() {
                return true;
            }
        });
    }
}

创建ServiceBuild类

在app的biz层中创建ServiceBuild类,主要是用来获取service的实例。

/**
 * 服务获取类
 */
public class ServiceBuild {
    /**
     * 用户服务
     */
    private static UserService mUserService;

    /**
     * 获取用户服务
     */
    public static synchronized UserService getUserService() {
        if (null == mUserService) {
            mUserService = BaseApi.createRetrofit().create(UserService.class);
        }
        return mUserService;
    }
}

更改Presenter代码

将LoginPresenter中创建Retrofit的代码和创建service的代码都去掉,换成调用ServiceBuild。

/**
 * 登陆Presenter
 */
public class LoginPresenter extends LoginContract.Presenter {

    private String TAG = "LoginPresenter";

    public LoginPresenter(LoginContract.View view) {
        super(view);
    }

    @Override
    public void login(String userName, String password) {
        // 开始请求
        Subscriber subscriber = ServiceBuild.getUserService()
                .login(userName, password)
                .subscribeOn(Schedulers.io())//运行在io线程
                .observeOn(AndroidSchedulers.mainThread())//回调在主线程
                .subscribeWith(new ResourceSubscriber<LoginDto>() {
                    @Override
                    public void onNext(LoginDto loginDto) {
                        //结果回调
                        Log.e(TAG, "onNext: " + loginDto);
                        if (loginDto.getCode() == 200) {
                            view.loginSuccess(loginDto);
                        } else {
                            view.loginFailure(loginDto.getMessage());
                        }

                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.e(TAG, "onError: ");
                        view.loginFailure("登陆失败:" + t.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        Log.e(TAG, "onComplete: ");
                    }
                });
    }
}

移除了大部分代码,Presenter看上去更加的简洁和优雅,接下来就运行起来看看效果吧。

查看效果

运行起来看看效果吧。

zEruuur.gif

效果和上章一致。

HttpLoggingInterceptor

HttpLoggingInterceptor 的作用可以说是非常的强大,使用这个拦截器,可以打印请求一切过程,包括请求头、请求体、请求参数、响应头等,对于接口对接中快速定位问题有很大的帮助,具体看看截图:

a6NjyuJ.png!web

结束

总结

  1. 分离了Retrofit的创建,减少了代码的冗余,利用接口来做配置项,灵活性大大提升。
  2. 独立了Service的创建,以后直接通过ServiceBuild的get方法进行获取。
  3. 虽然增加了一部分的代码,但是随着业务的增加,实则减少代码量。
  4. 大多数的Service都可以放到ServiceBuild中进行创建,方便统一管理。

问题

  1. RxJava线程转换还能进一步的封装。
  2. RxJava没有取消订阅。
  3. 订阅的时候重写的方法过于冗余,可以去除,做统一的订阅结果管理。

最后

本系列的课程还远着呢,就关于这个登陆都还能再写两篇文章,敬请期待。

源码-tag:0.3

软广

一个痴心妄想想成为一个全屏(栈)工程师的程序猿。

来来,关注一下吧!

RnA7Vra.jpg!web

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK