前提##
网络访问是一个App的基础功能,也是非常重要的一块;一般我们会使用一些第三方的网络组件,如:volley、okhttp,xutils,并通过一定的封装来实现网络请求;
我们的项目已经快3年了,从最初,简单的封装了一下 xutils的工具类,直到现在,一直在用,虽没出现过问题,但随着一些其他 网络库的 出现,想替换确因耦合度太高,造成无法替换;
通过上图,可以看到,只要我们想访问网络,我们直接去调用 封装的网络,而不管是在 界面UI,还是Service,或是MVP中的model,这样就造成了。在代码的任何处,都有可能看到网络请求的代码;
如下:
** 在activity中请求网络**
private void initData() {
NetWorkManager.request(this, NetworkConstant.API_GET_MSG_TYPE, new SimpleRequestCallback<String>(null, false, true) {
@Override
public void onStart() {
super.onStart();
// UI 上的控件
mSwipeRefreshLayout.setRefreshing(true);
}
@Override
public void onSuccess(ResponseInfo<String> 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<DetailSonModel> callback) {
Map<String, String> 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>(DetailSonModel.class) {
@Override
protected void onSuccess(DetailSonModel detailSonModel, List<DetailSonModel> 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层,去做网络请求的事情;
思考##
上面的层级结构,是有缺陷的:
- 网络模块耦合度太高,单独分离出来,困难;
- 另开一个App,代码无法直接复用,无法移植代码,造成重复开发;
MVP模式
在MVP模式中,是通过M去操作数据的(网络、数据库、缓存)都是在这块完成,但某个时候,迫于业务上开发压力,经常导致开发人员违反设计规范,直接在页面中操作任何数据;毕竟采用MVP模式开发,是会用大量的子类需要创建;
basenet模块
我们思考,通过一个 basenet 的组件去访问网络,这个 组件的目标就是纯网络访问,不涉及到任何业务(如:解析,加密等);一句话:来了请求,我就请求网络;此模块依赖于第三方的网络组件库,如:volley;
basenet通过接口来解耦,通过统一的抽象类 or 接口,对外实现网络请求,客户端,无须关注具体的实现类;
实践1(老套路,参数通过方法传)##
总体设计图
具体实现代码片段
// 请求接口
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<Pair<String, String>> headers, final List<Pair<String, String>> param, final IRequestCallBack callback);
void request(final int reqType, final String url, final List<Pair<String, String>> headers, final List<Pair<String, String>> param, final IRequestCallBack callback);
void request(final int reqType, final String url, final List<Pair<String, String>> headers, final List<Pair<String, String>> param, final IRequestCallBack callback, final long timeout);
}
// 回调接口
public interface IRequestCallBack<T> {
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<String, String> 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<String, String> 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<String, String> headers, final Map<String, String> 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<String>() {
@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<String, String> getHeaders() throws AuthFailureError {
Map<String, String> superHeader = super.getHeaders();
if (headers != null && headers.size() > 0) {
superHeader = headers;
}
return superHeader;
}
// 设置Body参数
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> 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<String, String> headers, Map<String, String> param, IRequestCallBack callback) {
this.request(reqType, url, headers, param, 0, callback);
}
@Override
public void request(int reqType, String url, Map<String, String> 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<String>() {
@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<String, String> mParams;
/**
* 请求头信息
*/
protected Map<String, String> 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<String, String> 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<String, String> 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<String, String> mParams;
/**
* 请求头信息
*/
private Map<String, String> 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<String, String> params) {
this.mParams = params;
return this;
}
public Builder headders(Map<String, String> 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<String>() {
@Override
public void onResponse(String response) {
mCallBack.onSuccess(response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
mCallBack.onFailure(error);
}
}) {
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> superHeader = super.getHeaders();
if (mHeader != null && mHeader.size() > 0) {
superHeader = mHeader;
}
return superHeader;
}
// 设置Body参数
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> 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);