网络访问组件的思考一(Volley)

前提##

网络访问是一个App的基础功能,也是非常重要的一块;一般我们会使用一些第三方的网络组件,如:volley、okhttp,xutils,并通过一定的封装来实现网络请求;

我们的项目已经快3年了,从最初,简单的封装了一下 xutils的工具类,直到现在,一直在用,虽没出现过问题,但随着一些其他 网络库的 出现,想替换确因耦合度太高,造成无法替换;

网络访问组件的思考一(Volley)_第1张图片
耦合度极高

通过上图,可以看到,只要我们想访问网络,我们直接去调用 封装的网络,而不管是在 界面UI,还是Service,或是MVP中的model,这样就造成了。在代码的任何处,都有可能看到网络请求的代码;
如下:
** 在activity中请求网络**

private void initData() {
        NetWorkManager.request(this, NetworkConstant.API_GET_MSG_TYPE, new SimpleRequestCallback(null, false, true) {
            @Override
            public void onStart() {
                super.onStart();
                // UI 上的控件
                mSwipeRefreshLayout.setRefreshing(true);
            }

            @Override
            public void onSuccess(ResponseInfo info) {
                super.onSuccess(info);
                mSwipeRefreshLayout.setRefreshing(false);
                ResponseParser parser = new ResponseParser(info.result, getActivity(), false);
                parser.parse(new ResponseParser.ParseCallback() {

在MVP模式(对应的M中访问)

class DetailSonRepo implements DetailSonContract.Repo {
    @Override
    public void loadData(String processInstanceId, String subCode, String subColumns, final LoadDataCallback callback) {
        Map params = new HashMap<>();
        params.put("processInstanceId", processInstanceId);
        params.put("subCode", subCode);
        params.put("subColumns", subColumns);

        NetWorkManager.request(this, NetworkConstant.API___, new SimpleReqCallbackAdapter<>(new AbsReqCallback(DetailSonModel.class) {
            @Override
            protected void onSuccess(DetailSonModel detailSonModel, List tArray, String rawData) {
                super.onSuccess(detailSonModel, tArray, rawData);
                callback.onDataLoaded(detailSonModel);
            }

            @Override
            public void onFailure(String errorMsg, int code) {
                super.onFailure(errorMsg);
                callback.onDataNotAvailable(errorMsg, code);
            }
        }), params);
    }

通过MVP模式,可将网络的请求,在M层搞定;避免了在界面UI层,去做网络请求的事情;

思考##

上面的层级结构,是有缺陷的:

  1. 网络模块耦合度太高,单独分离出来,困难;
  2. 另开一个App,代码无法直接复用,无法移植代码,造成重复开发;

MVP模式

在MVP模式中,是通过M去操作数据的(网络、数据库、缓存)都是在这块完成,但某个时候,迫于业务上开发压力,经常导致开发人员违反设计规范,直接在页面中操作任何数据;毕竟采用MVP模式开发,是会用大量的子类需要创建;

basenet模块
我们思考,通过一个 basenet 的组件去访问网络,这个 组件的目标就是纯网络访问,不涉及到任何业务(如:解析,加密等);一句话:来了请求,我就请求网络;此模块依赖于第三方的网络组件库,如:volley;
basenet通过接口来解耦,通过统一的抽象类 or 接口,对外实现网络请求,客户端,无须关注具体的实现类;

网络访问组件的思考一(Volley)_第2张图片
basenet

实践1(老套路,参数通过方法传)##

总体设计图

网络访问组件的思考一(Volley)_第3张图片
basenet整体设计图

具体实现代码片段

// 请求接口
public interface IRequest {

    public static final Short GET = 0;
    public static final Short POST = 1;

    /**
     * @param reqType 请求方式
     * @param url     地址
     * @param headers 请求头
     * @param param   请求参数
     */
    void request(final int reqType, final String url, final List> headers, final List> param, final IRequestCallBack callback);

    void request(final int reqType, final String url, final List> headers, final List> param, final IRequestCallBack callback);

    void request(final int reqType, final String url, final List> headers, final List> param, final IRequestCallBack callback, final long timeout);
}

// 回调接口
public interface IRequestCallBack {
    void onSuccess(T t);

    void onFailure(Throwable e);
}

请求抽象类

/**
 * 抽象类
 * Created by zhaoyu1 on 2017/3/6.
 */
public abstract class AbsRequest implements IRequest {

    public static final String CHAR_SET = "UTF-8";

    /**
     * 生成请求的url地址
     *
     * @param url
     * @param params
     * @return
     */
    protected String generateUrl(String url, Map params) {
        StringBuilder sb = new StringBuilder(url);
        if (params != null && params.size() > 0) {      // GET 请求,拼接url
            if (sb.charAt(sb.length() - 1) != '?') {            // get 请求 有 ?
                sb.append("?");
            }
            for (Map.Entry entry : params.entrySet()) {
                try {
                    sb.append(URLEncoder.encode(entry.getKey(), CHAR_SET)).append("=").append(URLEncoder.encode(entry.getValue(), CHAR_SET)).append("&");
                } catch (UnsupportedEncodingException e) {
                    // NOT_HAPPEND
                }
            }
            sb = sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

Volley Request 类

public class VolleyRequest extends AbsRequest {

    private RequestQueue requestQueue;
    private static VolleyRequest sRequest;

    public static VolleyRequest getInstance() {
        if (sRequest == null) {
            synchronized (VolleyRequest.class) {
                if (sRequest == null) {
                    sRequest = new VolleyRequest();
                    sRequest.requestQueue = Volley.newRequestQueue();
                }
            }
        }
        return sRequest;
    }

    @Override
    public void request(final int reqType, String url, final Map headers, final Map params, long timeout, final IRequestCallBack callback) {
        StringRequest stringRequest = null;
        int tReqType = Request.Method.GET;
        String tUrl = url;
        switch (reqType) {
            case RequestType.GET:
                tReqType = Request.Method.GET;
                tUrl = generateUrl(url, params);
                break;
            case RequestType.POST:
                tReqType = Request.Method.POST;
                break;
        }

        // 创建请求
        stringRequest = new StringRequest(tReqType, tUrl, new Response.Listener() {
            @Override
            public void onResponse(String response) {
                callback.onSuccess(response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                callback.onFailure(error.getCause());
            }
        }) {

            // 设置Header
            @Override
            public Map getHeaders() throws AuthFailureError {
                Map superHeader = super.getHeaders();
                if (headers != null && headers.size() > 0) {
                    superHeader = headers;
                }
                return superHeader;
            }

            // 设置Body参数
            @Override
            protected Map getParams() throws AuthFailureError {
                Map tParams = super.getParams();
                if (params != null && params.size() > 0) {
                    tParams = params;
                }
                return tParams;
            }
        };

        // 设置此次请求超时时间
        if (timeout > 1000) {
            stringRequest.setRetryPolicy(new DefaultRetryPolicy((int) timeout, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        }
        stringRequest.setTag(url);
        requestQueue.add(stringRequest);
    }

    @Override
    public void request(final int reqType, String url, Map headers, Map param, IRequestCallBack callback) {
        this.request(reqType, url, headers, param, 0, callback);
    }

    @Override
    public void request(int reqType, String url, Map params, IRequestCallBack callback) {
        this.request(reqType, url, null, params, 0, callback);
    }
}

客户端测试调用

    @Test
    public void testVolley() {
        Context appContext = InstrumentationRegistry.getTargetContext();
        getRequest(appContext).request(IRequest.RequestType.GET, "https://www.jd.com", null, new IRequestCallBack() {
            @Override
            public void onSuccess(String o) {
                Log.e("volley", o.toString());
            }

            @Override
            public void onFailure(Throwable e) {
            }
        });
        SystemClock.sleep(500);
    }

实践2(改由builder模式)##

经过上面的一系列步骤,我们完成了一个简单的基于Volley的请求封装,可以实现访问接口了;和同事讨论过后,觉得可用 Builder 建造者模式来重新构建,于是,经常改造,有了第二版,各种网络请求的参数,不再有 方法去传,而改由builder去构造;

修改后的接口

// IRequest 接口
public interface IRequest {

    interface RequestType {
        int GET = 0;
        int POST = 1;
    }

    /**
     * 执行请求,默认是 get 方式
     */
    void request();

    /**
     * 取消网络请求
     */
    void cancel();
}

// AbsRequest抽象类
public abstract class AbsRequest implements IRequest {

    public static final String CHAR_SET = "UTF-8";

    /**
     * url 地址
     */
    protected String mUrl;

    /**
     * 参数
     */
    protected Map mParams;

    /**
     * 请求头信息
     */
    protected Map mHeader;

    /**
     * 本地请求超时时间
     */
    protected long mTimeOut;

    /**
     * 请求标记
     */
    protected Object mTag;

    /**
     * 回调
     */
    protected IRequestCallBack mCallBack;

    /**
     * 请求方式
     */
    protected int mReqType;

    // 通过builder来构造
    protected AbsRequest(Builder builder) {
        this.mUrl = builder.mUrl;
        this.mCallBack = builder.mCallBack;
        this.mTag = builder.mTag;
        this.mTimeOut = builder.mTimeOut;
        this.mReqType = builder.mReqType;
        this.mParams = builder.mParams;
        this.mHeader = builder.mHeader;
    }

    @Override
    public final void request() {
        switch (mReqType) {
            case RequestType.GET:
                get();
                break;
            case RequestType.POST:
                post();
                break;
        }
    }

    /**
     * 执行get方式
     */
    protected abstract void get();

    /**
     * 执行post方式
     */
    protected abstract void post();


    /**
     * 生成请求的url地址
     *
     * @param url
     * @param params
     * @return
     */
    protected String generateUrl(String url, Map params) {
        StringBuilder sb = new StringBuilder(url);
        if (params != null && params.size() > 0) {      // GET 请求,拼接url
            if (sb.charAt(sb.length() - 1) != '?') {            // get 请求 有 ?
                sb.append("?");
            }
            for (Map.Entry entry : params.entrySet()) {
                try {
                    sb.append(URLEncoder.encode(entry.getKey(), CHAR_SET)).append("=").append(URLEncoder.encode(entry.getValue(), CHAR_SET)).append("&");
                } catch (UnsupportedEncodingException e) {
                    // NOT_HAPPEND
                }
            }
            sb = sb.deleteCharAt(sb.length() - 1);
        }
        return sb.toString();
    }

    // 抽象的建造者 Builder
    public static abstract class Builder {

        /**
         * url 地址
         */
        private String mUrl;

        /**
         * 参数
         */
        private Map mParams;

        /**
         * 请求头信息
         */
        private Map mHeader;

        /**
         * 本地请求超时时间
         */
        private long mTimeOut;

        /**
         * 请求标记
         */
        private Object mTag;

        /**
         * 回调
         */
        private IRequestCallBack mCallBack;

        /**
         * 请求方式
         */
        private int mReqType;

        public Builder() {
        }

        public Builder url(String url) {
            this.mUrl = url;
            return this;
        }

        public Builder body(Map params) {
            this.mParams = params;
            return this;
        }

        public Builder headders(Map headers) {
            this.mHeader = headers;
            return this;
        }

        public Builder timeout(long time) {
            this.mTimeOut = time;
            return this;
        }

        public Builder tag(Object tag) {
            this.mTag = tag;
            return this;
        }

        public Builder callback(IRequestCallBack callBack) {
            this.mCallBack = callBack;
            return this;
        }

        /**
         * @param reqType {@link IRequest.RequestType}中常量
         * @return
         */
        public Builder type(int reqType) {
            this.mReqType = reqType;
            return this;
        }

        public abstract AbsRequest build();
    }
}

接口我们看一下 修改后的Volley Request

public class VolleyRequest extends AbsRequest {

    private static RequestQueue requestQueue;
    private Request mRequest;

    private VolleyRequest(Builder builder) {
        super(builder);
    }

    @Override
    protected void get() {
        realRequest(Request.Method.GET);
    }

    @Override
    protected void post() {
        realRequest(Request.Method.POST);
    }

    private void realRequest(final int reqType) {
        int tReqType = Request.Method.GET;
        String tUrl = mUrl;
        switch (tReqType) {
            case Request.Method.GET:
                tReqType = Request.Method.GET;
                tUrl = generateUrl(mUrl, mParams);
                break;
            case Request.Method.POST:
                tReqType = Request.Method.POST;
                break;
        }

        mRequest = new StringRequest(tReqType, tUrl, new Response.Listener() {
            @Override
            public void onResponse(String response) {
                mCallBack.onSuccess(response);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mCallBack.onFailure(error);
            }
        }) {
            @Override
            public Map getHeaders() throws AuthFailureError {
                Map superHeader = super.getHeaders();
                if (mHeader != null && mHeader.size() > 0) {
                    superHeader = mHeader;
                }
                return superHeader;
            }

            // 设置Body参数
            @Override
            protected Map getParams() throws AuthFailureError {
                Map tParams = super.getParams();
                if (mParams != null && mParams.size() > 0 && reqType == Request.Method.POST) {
                    tParams = mParams;
                }
                return tParams;
            }
        };

        // 设置此次请求超时时间
        if (mTimeOut > 1000) {
            mRequest.setRetryPolicy(new DefaultRetryPolicy((int) mTimeOut, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
        }
        mRequest.setTag(mTag);
        requestQueue.add(mRequest);
    }

    @Override
    public void cancel() {
        if (mRequest != null) {
            mRequest.cancel();
        } else if (mTag != null) {
            requestQueue.cancelAll(mTag);
        }
    }

        // 实现建造者
    public static class Builder extends AbsRequest.Builder {

        private Context mCtx;
                // volley request 为单例
        private VolleyRequest sRequest;

        public Builder(Context ctx) {
            this.mCtx = ctx;
        }

        @Override
        public AbsRequest build() {
            if (sRequest == null) {
                synchronized (VolleyRequest.class) {
                    if (sRequest == null) {
                        sRequest = new VolleyRequest(this);
                        requestQueue = Volley.newRequestQueue(mCtx);
                    }
                }
            }
            return sRequest;
        }
    }
}

客户端的测试代码(简洁的链式编程):

 Context appContext = InstrumentationRegistry.getTargetContext();
 AbsRequest req = new VolleyRequest.Builder(appContext).url("http://www.jd.com")
                .timeout(2000).tag("hello")
                .callback(new IRequestCallBack() {
                    @Override
                    public void onSuccess(Object o) {
                        Log.e("volley", o.toString());
                    }

                    @Override
                    public void onFailure(Throwable e) {
                        Log.e("volley", e.toString());
                    }
                }).build();
        req.request();

        SystemClock.sleep(300);

你可能感兴趣的:(网络访问组件的思考一(Volley))