Android快速集成网络库功能(rxJava+retrofit+okhttp)

前言

现在的app都必不可少会用到网络请求,android原生也带有http请求的api,这里我们暂不讲,我们主要讲开源的网络请求解决方案,拥抱开源,本文在rxJava+retrofit+okhttp基础上分享如何封装集成适合于组件化开发的网络库。rxJava+retrofit+okhttp的详细用法大家可以自行查找相关资料。
说明:此文章适用于服务端采用微服务结构,如用户相关的采用USER服务(举例:主机请求地址为http://user.api.test.io),照片相关的采用IMG服务(举例:主机请求地址为http://img.api.test.io),客户端可切换不同的开发环境,如内网测试环境、外网测试环境,线上环境等,比如USER服务的内网测试环境主机请求地址为http://user.api.test.io,外网测试环境主机请求地址http://uat.user.api.test.org。

准备

  1. 在AndroidManifest.xml申明网络权限
<uses-permission android:name="android.permission.INTERNET" />

在app下的build.gradle申明相关依赖

    implementation "io.reactivex.rxjava2:rxjava:2.2.8"
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
    implementation 'com.squareup.okhttp3:okhttp:3.12.0'
    implementation 'com.google.code.gson:gson:2.8.5'

自定义订阅者ResourceSubscriber

没有加载提示

/**
 * 作者:lpx on 2020/3/20 10:27
 * Email : 1966353889@qq.com
 * Describe:没有加载提示的订阅者(结合网络请求使用)
 * update on 2020/3/23 15:06
 */
public class QuietSubscriber<T> extends ResourceSubscriber<T> {

    private OnNext<T> mNextListener;
    /**
     * 错误响应
     */
    private final String ERROR_RESPONSE = "{\"status\":404,\"msg\":\"请求失败\",\"data\":null}";
    /**
     * 网络错误返回10001
     */
    private final String ERROR_NET_WORD = "{\"status\":10001,\"msg\":\"网络异常\",\"data\":null}";
    /**
     * 数据解析错误
     */
    private final String ERROR_JSON_DATA = "{\"status\":10002,\"msg\":\"解析错误\",\"data\":null}";
    /**
     * 连接超时
     */
    private final String ERROR_CONNECT_TIMEOUT = "{\"status\":10003,\"msg\":\"连接超时\",\"data\":null}";
    /**
     * 请求超时
     */
    private final String ERROR_SOCKET_TIMEOUT = "{\"status\":10004,\"msg\":\"请求超时\",\"data\":null}";

    public QuietSubscriber(OnNext<T> nextListener) {
        this.mNextListener = nextListener;
    }


    @Override
    protected void onStart() {
        request(1);
    }

    /**
     * 对错误进行统一处理
     */
    @Override
    public void onError(Throwable e) {
        /*获取状态码非200(出现错误)时的response里的body*/
        if (e instanceof HttpException) {
            ResponseBody body = ((HttpException) e).response().errorBody();
            try {
                String json = body.string();
                /*判断是否是有效的Json数据*/
                if (isJson(json)) {
                    Response response = new Gson().fromJson(json, Response.class);
                    if (response != null) {
                        /*等于请求响应回调*/
                        if (mNextListener instanceof OnNextOnError) {
                            ((OnNextOnError) mNextListener).onError(response);
                        }
                        return;
                    }
                }
                formJson(ERROR_RESPONSE);

            } catch (IOException ioE) {
                ioE.printStackTrace();
            }

        } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
            /*网络错误返回的异常*/
            formJson(ERROR_NET_WORD);

        } else if (e instanceof JsonSyntaxException) {
            /*保证返回的异常为json数据*/
            formJson(ERROR_JSON_DATA);

        } else if (e instanceof ConnectTimeoutException) {
            /*连接超时*/
            formJson(ERROR_CONNECT_TIMEOUT);

        } else if (e instanceof SocketTimeoutException) {
            /*请求超时*/
            formJson(ERROR_SOCKET_TIMEOUT);

        } else {
            /*保证返回的异常为json数据*/
            formJson(ERROR_RESPONSE);
        }
    }

    @Override
    public void onComplete() {

    }

    @Override
    public void onNext(T t) {
        handle(t);
    }
    /**
     * 判断是否是json数据
     */
    private boolean isJson(String json) {
        try {
            new JsonParser().parse(json);
            return true;
        } catch (JsonParseException e) {
            return false;
        }
    }

    private void formJson(String json) {
        if (mNextListener instanceof OnNextOnError) {
            Response response = new Gson().fromJson(json, Response.class);
            ((OnNextOnError) mNextListener).onError(response);
        }
    }
    
    /**
     * 这里还需要判断发回的数据是否合法
     * 将onNext方法中的返回结果交给网络调用者自己处理
     */
    public void handle(T t) {
        if (mNextListener == null) {
            onError(new Exception("The call nextListener is null"));
            return;
        }
        /*返回的数据result*/
        mNextListener.onNext(t);
    }
}

有加载提示

/**
 * 作者:lpx on 2020/3/20 14:00
 * Email : 1966353889@qq.com
 * Describe:有加载提示的订阅者,后台加载,开始加载时显示提示框,结束时隐藏提示框
 */
public class ProgressSubscriber<T> extends QuietSubscriber<T> {

    /**
     * 进度条的实现类
     */
    private IFDialog dialogImp;

    public ProgressSubscriber(IFDialog dialogImp, OnNext<T> nextListenter) {
        super(nextListenter);
        this.dialogImp = dialogImp;
    }

    /**
     * 订阅开始时调用
     * 显示ProgressDialog
     */
    @Override
    public void onStart() {
        request(1);
        dialogImp.showLoading();
    }


    /**
     * 对错误进行统一处理
     * 隐藏ProgressDialog
     *
     * @param e
     */
    @Override
    public void onError(final Throwable e) {
        dialogImp.dismissLoading();
        super.onError(e);
    }

    /**
     * 将onNext方法中的返回结果交给Activity或Fragment自己处理
     */
    @Override
    public void onNext(final T t) {
        /*隐藏ProgressDialog*/
        dialogImp.dismissLoading();
        handle(t);

    }

}

请求提示实现Interface

/**
 * 作者:lpx on 2020/3/19 14:06
 * Email : 1966353889@qq.com
 * Describe:网络请求加载提示回调接口
 */
public interface IFDialog {

    /**
     * 显示加载提示
     */
    void showLoading();

    /**
     * 隐藏加载提示
     */
    void dismissLoading();
}

自定义网络请求响应体

这里可以根据项目中接口返回数据的实际情况调整

/**
 * 作者:lpx on 2020/3/19 11:23
 * Email : 1966353889@qq.com
 */
public class Response<T> implements Serializable {

    public Integer status = -1;
    public Object msg;
    public T data;
    
    public Integer status() {
        return status;
    }

    public String getMsg() {
        if (msg == null) {
            return "";
        } else {
            if (msg instanceof String) {
                return (String) msg;
            } else {
                return "";
            }
        }
    }

    public Object data() {
        return data;
    }
}

网络请求回调监听

只包含成功

/**
 * 作者:lpx on 2020/3/19 14:15
 * Email : 1966353889@qq.com
 * Describe:网络请求结果回调接口(只包含成功)
 */
public interface OnNext<T> {
    /**
     * @param t 网络请求结果成功(响应码为200)时将返回的Json解析
     *          后对应的实体类,注:如T的类型为Object,调toString()
     *          后json各字段名和对应的的值是不带引号的,如需要看到引号则需调Gson.toJson。
     *          如果采用JsonObject则不存在这个问题,注意是JsonObject不是JSONObject。
     * */
    void onNext(T t);
}

包含成功和失败

/**
 * 作者:lpx on 2020/3/19 14:16
 * Email : 1966353889@qq.com
 * Describe:网络请求结果回调接口(包含成功和失败)
 */
public interface OnNextOnError<T> extends OnNext<T> {
    void onError(Response errorResponse);
}

接下来就是标准的retrofit的用法了

新增一个网络请求工具类

配合rxjava订阅网络请求

/**
 * 作者:lpx on 2020/3/20 14:00
 * Email : 1966353889@qq.com
 * update on 2020/3/23 15:06
 */
public class RxUtils {

    /**
     * 支持回调加载提示的的网络请求
     *
     * @param dialogImp    加载提示接口
     * @param flowable     Flowable是为了更好地处理背压问题而新设计的类以弥补Observable的不足,
     *                     也就是说,Observable不支持背压处理,一旦未及时处理的事件数累积到一定
     *                     程度就会产生MissingBackpressureException或者OutOfMemoryError。
     * @param nextListener 网络请求回调事件监听
     */
    public static <T> Disposable rx(IFDialog dialogImp, Flowable<T> flowable, OnNext<T> nextListener) {
        return flowable
                .onBackpressureDrop()  //加上背压策略
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new ProgressSubscriber<>(dialogImp, nextListener));
    }

    /**
     * 不支持回调加载提示的的网络请求
     *
     * @param flowable     Flowable是为了更好地处理背压问题而新设计的类以弥补Observable的不足,
     *                     也就是说,Observable不支持背压处理,一旦未及时处理的事件数累积到一定
     *                     程度就会产生MissingBackpressureException或者OutOfMemoryError。
     * @param nextListener 网络请求回调事件监听
     */
    public static <T> Disposable rx(Flowable<T> flowable, OnNext<T> nextListener) {
        return flowable
                .onBackpressureDrop()  //加上背压策略
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(new QuietSubscriber<>(nextListener));
    }

    /**
     * 创建请求体,POST、PUT、PATCH请求都需要设置
     *
     * @param data 请求体中的数据 ,json格式
     * @return
     */
    public static RequestBody createRequestBody(String data) {
        return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), data);
    }

    /**
     * 创建请求体,POST、PUT、PATCH请求都需要设置
     *
     * @return
     */
    public static RequestBody createRequestBody(Map<String, Object> params) {
        String json = GsonTools.toJson(params);
        return RxUtils.createRequestBody(json);
    }

}

新建网络请求入口基类

对应retrofit的请求api

/**
 * 作者:lpx on 2020/3/19 10:55
 * Email : 1966353889@qq.com
 * Describe:网络请求入口基类,子类实现具体的接口请求服务
 * update on 2020/4/17 16:01
 */
public abstract class BaseApi<T> {
    /**
     * 子类实现,返回对应接口请求服务的class,如xx.class
     */
    public abstract Class<T> getHttpServiceClass();

    /**
     * 网络请求服务接口(框架限制不能extends其他的interface)
     * API interfaces must not extend other interfaces.
     */
    private T mService;

    /**
     * 返回HService
     */
    public T getService() {
        if (mService == null) {
            synchronized (BaseApi.class) {
                if (mService == null) {
                    Retrofit retrofit;
                    /*OkHttp设置*/
                    OkHttpClient.Builder builder = new OkHttpClient.Builder();
                    builder.retryOnConnectionFailure(true)//请求连接失败后重连
                            .connectTimeout(connectTimeout(), timeUnit())//设置连接超时时间
                            .readTimeout(readTimeout(), timeUnit())//设置读取超时时间
                            .writeTimeout(writeTimeout(), timeUnit())//设置写入超时时间
                            .addInterceptor(new HostInterceptor())//设置host拦截
                            .addInterceptor(new HeaderInterceptor());//设置header拦截
                    /*这里的baseUrl的值无任何实际作用,但是retrofit实例一定需要先设置这个值且不为null或“”*/
                    retrofit = new Retrofit.Builder()
                            .baseUrl(Constant.baseUrl)
                            .client(builder.build())
                            .addConverterFactory(GsonConverterFactory.create())//gson转换工厂
                            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())//解析rxjava的适配器
                            .build();
                    mService = retrofit.create(getHttpServiceClass());

                }
            }
        }
        return mService;
    }

    /**
     * 连接超时时间(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public long connectTimeout() {
        return NetworkConfig.getInstance().getConnectTimeout();
    }

    /**
     * 读取超时时间(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public long readTimeout() {
        return NetworkConfig.getInstance().getReadTimeout();
    }

    /**
     * 写入超时时间(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public long writeTimeout() {
        return NetworkConfig.getInstance().getWriteTimeout();
    }

    /**
     * 超时时间对应的时间单位(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public TimeUnit timeUnit() {
        return NetworkConfig.getInstance().getTimeUnit();
    }
}

新建网络请求全局配置NetworkConfig

/**
 * 作者:lpx on 2020/4/13 11:42
 * Email : 1966353889@qq.com
 * Describe:外部设置全局Headers(一般是主工程使用,网络库优先使用设置的全局Headers)
 * update on 2020/4/13 13:57
 */
public class NetworkConfig {
    private static NetworkConfig mInstance;
    private Map<String, String> headers;
    /**
     * 连接超时时间
     */
    private long connectTimeout = 10;
    /**
     * 读取超时时间
     */
    private long readTimeout = 20;
    /**
     * 写入超时时间
     */
    private long writeTimeout = 20;
    /**
     * 超时时间对应的时间单位
     */
    private TimeUnit timeUnit = TimeUnit.SECONDS;
    private BaseServer mServer;

    private OnResetTokenListener mResetTokenListener;

    private NetworkConfig() {
        headers = new HashMap<>();
    }

    /**
     * 对外提供的GlobalHeaders实例
     */
    public static NetworkConfig getInstance() {
        if (mInstance == null) {
            synchronized (NetworkConfig.class) {
                if (mInstance == null) {
                    mInstance = new NetworkConfig();
                }
            }
        }
        return mInstance;
    }

    /**
     * 设置全局请求头(全部覆盖,一般是主工程调用)
     */
    public void headers(Map<String, String> headers) {
        this.headers = headers;
    }

    /**
     * 设置全局请求头(单个覆盖,一般是主工程调用)
     */
    public void header(String key, String value) {
        headers.put(key, value);
    }

    /**
     * 获取全局请求头(一般是主工程调用)
     */
    public Map<String, String> getHeaders() {
        return headers;
    }

    /**
     * 获取某个请求头
     */
    public String getHeader(String key) {
        return headers != null ? headers.get(key) : null;
    }

    /**
     * 获取连接超时时间
     */
    public long getConnectTimeout() {
        return connectTimeout;
    }

    /**
     * 设置连接超时时间
     */
    public void setConnectTimeout(long connectTimeout) {
        this.connectTimeout = connectTimeout;
    }

    /**
     * 获取读取超时时间
     */
    public long getReadTimeout() {
        return readTimeout;
    }

    /**
     * 设置读取超时时间
     */
    public void setReadTimeout(long readTimeout) {
        this.readTimeout = readTimeout;
    }

    /**
     * 获取写入超时时间
     */
    public long getWriteTimeout() {
        return writeTimeout;
    }

    /**
     * 设置写入超时时间
     */
    public void setWriteTimeout(long writeTimeout) {
        this.writeTimeout = writeTimeout;
    }

    /**
     * 获取超时时间对应的时间单位
     */
    public TimeUnit getTimeUnit() {
        return timeUnit;
    }

    /**
     * 设置超时时间对应的时间单位
     */
    public void setTimeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
    }

    /**
     * 获取请求环境
     */
    public BaseServer getServer() {
        if (mServer == null) {
            mServer = new IoServer();
        }
        return mServer;
    }

    /**
     * 设置请求环境
     */
    public void setServer(BaseServer mServer) {
        this.mServer = mServer;
    }
}

新建网络环境基类

/**
 * 作者:lpx on 2020/3/18 17:24
 * Email : 1966353889@qq.com
 * Describe:常量接口(辅助网络请求)
 */
public interface Constant {
    String baseUrl = "https://api.test.com/";
    String IO = "io";
    String ORG = "org";
    String ORI = "ori";

    /*不同的接口服务*/
    interface Request {
        String HOST_TYPE = "host_type";
        int CARD = 1;
        int CONFIG = 2;
        int COUPONS = 3;
        int DISPATCH = 4;
        int FEED = 5;
        int MP = 6;
        int IMG = 7;
        int ACCOUNT = 8;
        int MSG = 9;
        int ORDER = 10;
        int PAGE = 11;
        int PAY = 12;
        int REPORT = 13;
        int SEARCH = 14;
        int TRAFFIC = 15;
        int USER = 16;
        int DATA = 17;
        int ZHIDAO = 18;
    }

}

/**
 * 作者:lpx on 2020/3/18 17:28
 * Email : 1966353889@qq.com
 * Describe:网络请求服务配置基类
 */
public abstract class BaseServer {

    public String getUrl(int value) {
        String url = "";
        switch (value) {
            case Constant.Request.CARD:
                url = card();
                break;
            case Constant.Request.CONFIG:
                url = config();
                break;
            case Constant.Request.DISPATCH:
                url = dispatch();
                break;
            case Constant.Request.FEED:
                url = feed();
                break;
            case Constant.Request.MP:
                url = mp();
                break;
            case Constant.Request.IMG:
                url = img();
                break;
            case Constant.Request.ACCOUNT:
                url = account();
                break;
            case Constant.Request.MSG:
                url = msg();
                break;
            case Constant.Request.ORDER:
                url = order();
                break;
            case Constant.Request.PAGE:
                url = page();
                break;
            case Constant.Request.PAY:
                url = pay();
                break;
            case Constant.Request.REPORT:
                url = report();
                break;
            case Constant.Request.SEARCH:
                url = search();
                break;
            case Constant.Request.TRAFFIC:
                url = traffic();
                break;
            case Constant.Request.USER:
                url = user();
                break;
            case Constant.Request.DATA:
                url = data();
                break;
            case Constant.Request.ZHIDAO:
                url = zhidao();
                break;
        }
        return url;
    }

    protected abstract String card();

    protected abstract String config();

    protected abstract String dispatch();

    protected abstract String feed();

    protected abstract String mp();

    protected abstract String img();

    protected abstract String account();

    protected abstract String msg();

    protected abstract String order();

    protected abstract String page();

    protected abstract String pay();

    protected abstract String report();

    protected abstract String search();

    protected abstract String traffic();

    protected abstract String user();

    protected abstract String data();

    protected abstract String zhidao();
}

实现内网测试环境网络请求服务配置

/**
 * 作者:lpx on 2020/3/18 17:31
 * Email : 1966353889@qq.com
 * Describe:io环境(内网)网络请求服务配置
 */
public final class IoServer extends BaseServer {
    @Override
    protected String card() {
        return "http://card.api.test.io/";
    }

    @Override
    protected String config() {
        return "http://config.api.test.io/";
    }

    @Override
    protected String dispatch() {
        return "http://dispatch.api.test.io/";
    }

    @Override
    protected String feed() {
        return "http://feed.api.test.io/";
    }

    @Override
    protected String mp() {
        return "http://mp.test.io/";
    }

    @Override
    protected String img() {
        return "http://img.test.io/";
    }

    @Override
    protected String account() {
        return "http://account.api.test.io/";
    }

    @Override
    protected String msg() {
        return "http://msg.api.test.io/";
    }

    @Override
    protected String order() {
        return "http://order.api.test.io/";
    }

    @Override
    protected String page() {
        return "http://page.test.io/";
    }

    @Override
    protected String pay() {
        return "http://pay.api.test.io/";
    }

    @Override
    protected String report() {
        return "http://report.api.test.io/";
    }

    @Override
    protected String search() {
        return "http://search.api.test.io/";
    }

    @Override
    protected String traffic() {
        return "http://traffic.api.test.io/";
    }

    @Override
    protected String user() {
        return "http://user.api.test.io/";
    }

    @Override
    protected String data() {
        return "http://data.test.com/";
    }

    @Override
    protected String zhidao() {
        return "http://zhidao.api.test.io/";
    }
}

外网测试环境OrgServer和线上环境OriServer的配置同内网实现方式一样,都是继承BaseServer实现对应的方法。

新建具体的Retrofit接口请求服务(示例)

/**
 * 作者:lpx on 2020/3/19 11:25
 * Email : 1966353889@qq.com
 * Describe:具体的接口请求服务(示例)
 * 使用@Field 与 @FieldMap时,出现@Field parameters can only be used with form encoding.需在对应的方法上加上@FormUrlEncoded
 */
public interface ServiceSample {
    /**
     * 获取用户信息
     */
    @Headers(Constant.Request.HOST_TYPE + ":" + Constant.Request.USER)
    @GET("profile/")
    Flowable<Response<UserInfo>> getUserInfo();
    /**
     * 退出登录
     */
    @Headers(Constant.Request.HOST_TYPE + ":" + Constant.Request.ACCOUNT)
    @POST("logout/")
    Flowable<Response<JsonObject>> logout(@Body RequestBody requestBody
    );
}

新建Retrofit网络请求入口

/**
 * 作者:lpx on 2020/4/7 18:51
 * Email : 1966353889@qq.com
 * Describe: 网络请求入口
 * update on 2020/4/20 11:14
 */
public class LoginApi extends BaseApi<LoginService> {
    private static LoginApi mInstance;

    @Override
    public Class<LoginService> getHttpServiceClass() {
        return LoginService.class;
    }

    public static LoginApi getInstance() {
        if (mInstance == null) {
            synchronized (LoginApi.class) {
                if (mInstance == null) {
                    mInstance = new LoginApi();

                }
            }
        }
        return mInstance;
    }
}

上边BaseApi提到了connectTimeout、readTimeout、writeTimeout和timeUnit默认在NetworkConfig实现,如需另外实现也可重写方法,如

/**
 * 作者:lpx on 2020/4/7 18:51
 * Email : 1966353889@qq.com
 * Describe: 网络请求入口
 * update on 2020/4/20 11:14
 */
public class LoginApi extends BaseApi<LoginService> {
    private static LoginApi mInstance;

    @Override
    public Class<LoginService> getHttpServiceClass() {
        return LoginService.class;
    }

    public static LoginApi getInstance() {
        if (mInstance == null) {
            synchronized (LoginApi.class) {
                if (mInstance == null) {
                    mInstance = new LoginApi();

                }
            }
        }
        return mInstance;
    }
    /**
     * 连接超时时间(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public long connectTimeout() {
        return 30;
    }

    /**
     * 读取超时时间(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public long readTimeout() {
        return 30;
    }

    /**
     * 写入超时时间(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public long writeTimeout() {
        return 30;
    }

    /**
     * 超时时间对应的时间单位(默认返回NetworkConfig的实现,也可在子类重写)
     */
    public TimeUnit timeUnit() {
        return TimeUnit.SECONDS;
    }
}

先来看下我们怎么把对应的网络请求设置在对应的请求环境。这里我们使用Okhttp的Interceptor来实现,我们的ServiceSample中每个请求都设置了表示是什么服务的请求头headers,如@Headers(Constant.Request.HOST_TYPE + “:” + Constant.Request.USER)表示USER服务,Constant.Request.HOST_TYPE作为服务的key标识,我们可在Interceptor中遍历headers拿到key为Constant.Request.HOST_TYPE的header值,就可以知道该请求是走的什么服务了,在通过我们自定义的BaseServer就可以拿到对应的主机请求地址。

自定义HostInterceptor

/**
 * 作者:lpx on 2020/4/7 18:51
 * Email : 1966353889@qq.com
 * Describe: 网络请求拦截器,用于动态修改请求的host
 * update on 2020/4/20 11:14
 */
public class HostInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        /*获取request*/
        Request request = chain.request();
        /*获取request的创建者builder*/
        Request.Builder builder = request.newBuilder()/*.removeHeader("Accept-Encoding")*/;
        /*从request中获取headers,通过给定的键url_name*/
        List<String> headerValues = request.headers(Constant.Request.HOST_TYPE);
        if (headerValues != null && headerValues.size() > 0) {
            /*如果有这个header,先将配置的header删除,因此header仅用作app和okhttp之间使用*/
            builder.removeHeader(Constant.Request.HOST_TYPE);
            /*匹配获得新的BaseUrl*/
            String headerValue = headerValues.get(0);
            HttpUrl newBaseUrl = HttpUrl.parse(getServer().getUrl(Integer.parseInt(headerValue)));
            /*从request中获取原有的HttpUrl实例oldHttpUrl*/
            HttpUrl oldHttpUrl = request.url();
            /*重建新的HttpUrl,修改需要修改的url部分*/
            HttpUrl newFullUrl = oldHttpUrl
                    .newBuilder()
                    .scheme(newBaseUrl.scheme())
                    .host(newBaseUrl.host())
                    .port(newBaseUrl.port())
                    .build();
            /*重建这个request,通过builder.url(newFullUrl).build();*/
            /*然后返回一个response至此结束修改*/
            return chain.proceed(builder.url(newFullUrl).build());
        } else {
            return chain.proceed(builder.build());
        }
    }

    /**
     * 子类实现,返回对应build环境的BaseServer
     */
    private BaseServer getServer() {
        return NetworkConfig.getInstance().getServer();
    }

}

从HostInterceptor我们发现请求环境是通过getServer()方法获取的,getServer()方法返回NetworkConfig.getInstance().getServer();我们也可以提前通过NetworkConfig.getInstance().setServer(BaseServer baseServer);设置我们的请求环境,如NetworkConfig.getInstance().setServer(new IoServer());将当前请求环境设置为内网请求环境等。

我们新建一个Interceptor用于统一添加网络请求的请求头:

/**
 * 作者:lpx on 2020/4/7 18:51
 * Email : 1966353889@qq.com
 * Describe:全局请求头拦截器
 * update on 2020/4/20 11:14
 */
public class HeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request.Builder mBuilder = chain.request().newBuilder().removeHeader("Accept-Encoding");
        Map<String, String> mHeadersMap = getHeaders();
        if (mHeadersMap != null) {
            for (Map.Entry<String, String> entry : mHeadersMap.entrySet()) {
                if (!TextUtils.isEmpty(entry.getValue())) {
                    mBuilder.header(entry.getKey(), entry.getValue());
                }
            }
        }
        return chain.proceed(mBuilder.build());
    }

    /**
     * 获取要添加的请求头(通过NetworkConfig获取,具体设置请求头可查看NetworkConfig类)
     */
    private Map<String, String> getHeaders() {
        return NetworkConfig.getInstance().getHeaders();
    }

}

这里我们举例发起一次网络请求

如获取用户信息
  1. 只关心成功结果
RxUtils.rx(LoginApi.getInstance().getService().getUserInfo(), new OnNext<Response<UserInfo>>() {
            @Override
            public void onNext(Response<UserInfo> userInfoResponse) {
                /*请求成功*/
                /*mUserInfo为用户信息对应实体*/
                /*这里的回调参数Response<UserInfo> userInfoResponse对应ServiceSample的getUserInfo()的返回值*/
                UserInfo mUserInfo=userInfoResponse.data;
            }
        });
  1. 既关心成功结果也关心失败结果
RxUtils.rx(LoginApi.getInstance().getService().getUserInfo(), new OnNextOnError<Response<UserInfo>>() {
            @Override
            public void onError(Response response) {
                /*请求失败*/
                Toast.makeText(context,response.getMsg(),Toast.LENGTH_LONG).show();
            }

            @Override
            public void onNext(Response<UserInfo> userInfoResponse) {
                /*请求成功*/
                /*mUserInfo为用户信息对应实体*/
                /*这里的回调参数Response<UserInfo> userInfoResponse对应ServiceSample的getUserInfo()的返回值*/
                UserInfo mUserInfo=userInfoResponse.data;
            }
        });
  1. 只关心成功结果(带加载提示,可用于实现加载loading)
RxUtils.rx(new IFDialog() {
            @Override
            public void showLoading() {
                /*可在这里实现loading效果,如弹出带loading效果的Dialog*/
            }

            @Override
            public void dismissLoading() {
                /*可在这里关闭loading效果,如dismiss带loading效果的Dialog*/
            }
        }, LoginApi.getInstance().getService().getUserInfo(), new OnNext<Response<UserInfo>>() {
            @Override
            public void onNext(Response<UserInfo> userInfoResponse) {
                /*请求成功*/
                /*mUserInfo为用户信息对应实体*/
                /*这里的回调参数Response<UserInfo> userInfoResponse对应ServiceSample的getUserInfo()的返回值*/
                UserInfo mUserInfo=userInfoResponse.data;
            }
        });
  1. 既关心成功结果也关心失败结果(带加载提示,可用于实现加载loading)`
RxUtils.rx(new IFDialog() {
            @Override
            public void showLoading() {
                /*可在这里实现loading效果,如弹出带loading效果的Dialog*/
            }

            @Override
            public void dismissLoading() {
                /*可在这里关闭loading效果,如dismiss带loading效果的Dialog*/
            }
        }, LoginApi.getInstance().getService().getUserInfo(), new OnNextOnError<Response<UserInfo>>() {
            @Override
            public void onError(Response response) {
                /*请求失败*/
                Toast.makeText(context,response.getMsg(),Toast.LENGTH_LONG).show();
            }

            @Override
            public void onNext(Response<UserInfo> userInfoResponse) {
                /*请求成功*/
                /*mUserInfo为用户信息对应实体*/
                /*这里的回调参数Response<UserInfo> userInfoResponse对应ServiceSample的getUserInfo()的返回值*/
                UserInfo mUserInfo=userInfoResponse.data;
            }
        });

组件化

如果我们有组件化的需求,前边的网络请求的相关类可以放在module中,对应的BaseServer根据项目自行更改调整,NetworkConfig自行配置好,每个项目(这里我们举例登录组件)都需要自定义一个LoginApi,LoginService,IoServer,OrgServer,OriServer,在我们需要用到网络请求的项目工程中把这个module依赖进去,或者将module打包成aar文件给需要用到的项目implementation进去,就可以快速拥有网络库的功能了。

结尾

是不是很方便快捷,希望本文可以帮助到您,也希望各位不吝赐教,提出您在使用中的宝贵意见,谢谢。
如果可以的话,也可以扫一扫下方的二维码请作者喝一杯奶茶哈
在这里插入图片描述
谢谢您的观看。
有问题可发送至:1966353889@qq.com
欢迎交流,共同进步。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值