一、背景
开发当中,在请求网络的时候,大家或多或少都会使用一些第三方框架,Android-Async-Http、 Volley、XUtils、Okhttp、Retrofit 等。这些框架减少了我们的很多工作量,同时也对侵入了我们的项目。
大家回顾一下手头上的项目代码,是不是或多或少存在这样那样的历史遗留问题,第三方框架调用混乱,没有封装,或者封装不彻底。如果要替换框架,很有可能要对项目大动干戈。
二、封装的必要性
- 随着需求的变更或者时间的迁移,某些框架可能已经不能满足我们的需求,我们需要使用新的框架来代替
- 对第三方框架进行封装,是为了达到对模块项目的控制,已最小的代价替换框架,达到对项目的控制
三、封装网络库示例
我们先来看一下 okHttp 的使用:
OkHttpClient.Builder mBuilder= new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.cache(new Cache(mContext.getExternalFilesDir("okhttp"),cacheSize));
client=mBuilder.build();
Request.Builder builder = new Request.Builder().url(url).tag(option.mTag);
builder = configHeaders(builder,option);
Request build = builder.build();
client.newCall(build).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handleError(e, iResponseListener);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
handleResult(response, iResponseListener);
}
});
如果不进行封装,okHttp 请求网络大概是这样的,想一下,如果我们在项目中都这样使用,要替换框架,那花费的工作量要多大。
不过这种方法,在项目中大多数人不会这样使用,至少都会封装成为一个工具类。封装完成之后如下:
public static void doGet(Context context,String url, Map<String, String> paramsMap, NetworkOption networkOption,final IResponseListener iResponseListener){
OkHttpClient.Builder mBuilder= new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.cache(new Cache(context.getExternalFilesDir("okhttp"),cacheSize));
OkHttpClient cilent = mBuilder.build();
Request.Builder builder = new Request.Builder().url(url);
Request build = builder.build();
cilent.newCall(build).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handleError(e, iResponseListener);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
handleResult(response, iResponseListener);
}
});
}
public static void doPost(Context context,String url, Map<String, String> paramsMap, NetworkOption networkOption, final IResponseListener iResponseListener) {
OkHttpClient.Builder mBuilder= new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.cache(new Cache(context.getExternalFilesDir("okhttp"),cacheSize));
OkHttpClient cilent = mBuilder.build();
url= NetUtils.checkUrl(url);
final NetworkOption option=NetUtils.checkNetworkOption(networkOption,url);
FormBody.Builder builder = new FormBody.Builder();
FormBody formBody = builder.build();
Request.Builder requestBuilder = new Request.Builder().url(url).post(formBody).tag(option.mTag);
Request request = requestBuilder.build();
cilent.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handleError(e,iResponseListener);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
handleResult(response,iResponseListener);
}
});
}
这种封装成工具类的比完全没有封装的好了很多,但是还是存在一定的问题的。封装成工具类的话,别人完全有权限访问你这个工具类,他可以随时修改你工具类里面的实现,这给维护带来了一定的成本。那有没有更好的方法呢? 大多数人都会想到的是封装统一网络接口,没错,确实是这样。于是,经过一番思考以后,我们可能写出以下的代码。
void doGet(Context context,String url, final Map<String, String> paramsMap, final IResponseListener iResponseListener);
如果我们需要动态配置请求头呢,请求 TAG 呢,这时候你会怎么写,继续增加参数吗?
void doGet(Context context,String url, final Map<String, String> paramsMap,final Map<String, String> headMap, String tag,final IResponseListener iResponseListener);
那以后如果要配置缓存路径呢,配置请求超时时间,读取超时时间呢,直接在方法中增加相应的参数?
这样的做法是不太明智的,会导致接口越来越臃肿。
既然这样,那有没有办法解决呢?
首先,我们先回想一下,网络请求那些参数是必要的,那些是非必要的,即可有可无的。
必要选项
- url,请求网址
- paramsMap ,请求参数
- iResponseListener 请求结果的回调
非必要选项
- context 通常是用来配置配置一些缓存等一些信息
- headMap 请求头
- tag 请求 TAG,用来区分或者取消网络请求
- connectTimeout 连接超时时间
- readTimeout 读取超时时间
- writeTimeout 写入超时时间
了解完必要参数和非必要参数之后,我们的接口要怎样提取呢?
不知道大家有没有注意到 okHttpClient 的构建,他将所有的网络配置都提取封装在 OkHttpClient,Request 中,在请求网络的时候减少了相应的参数,简洁灵活。
OkHttpClient.Builder mBuilder= new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(new LoggingInterceptor())
.cache(new Cache(context.getExternalFilesDir("okhttp"),cacheSize));
OkHttpClient cilent = mBuilder.build();
Request.Builder builder = new Request.Builder().url(url);
client.newCall(builder.build()).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handleError(e, iResponseListener);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
handleResult(response, iResponseListener);
}
});
看了 OKhttp 的代码,我们也可以依样画葫芦,我们可以将非必要参数封装在一个实体类 NetworkOption 当中,必要的参数作为方法参数,这样接口变成以下的形式
void doGet(String url, final Map<String, String> paramsMap, final IResponseListener iResponseListener);
void doGet(String url, final Map<String, String> paramsMap, NetworkOption networkOption, final IResponseListener iResponseListener);
对比直接在方法中增加相应的参数,是不是简洁很多。
接着,我们一起来看一下 NetworkOption 的属性。基本上,只要 okhttp 可以配置的,我们都可以往里面配置。这里列举了一些常用的字段 ,baseUrl,请求标志 tag,请求头 mHeaders。-connectTimeout 连接超时时间,readTimeout 读取超时时间,writeTimeout 写入超时时间就不一一列举了。
public class NetworkOption {
public String mBaseUrl;
public String mTag;
public Map<String,String> mHeaders;
public NetworkOption(String tag) {
this.mTag = tag;
}
public static final class Builder{
public String tag;
public Map<String,String> mHeaders;
public String mBaseUrl;
public Builder setTag(String tag){
this.tag=tag;
return this;
}
public Builder setHeaders(Map<String,String> headers){
mHeaders=headers;
return this;
}
public Builder setBaseUrl(String baseUrl) {
mBaseUrl = baseUrl;
return this;
}
public NetworkOption build(){
NetworkOption networkOption = new NetworkOption(tag);
networkOption.mHeaders=mHeaders;
networkOption.mBaseUrl=mBaseUrl;
return networkOption;
}
}
}
最后的封装实现
public interface NetRequest {
void init(Context context);
void doGet(String url, final Map<String, String> paramsMap, final IResponseListener iResponseListener);
void doGet(String url, final Map<String, String> paramsMap, NetworkOption networkOption, final IResponseListener iResponseListener);
void doPost(String url, final Map<String, String> paramsMap, final IResponseListener iResponseListener);
void doPost(String url, final Map<String, String> paramsMap, NetworkOption networkOption,final IResponseListener iResponseListener);
void cancel(Object tag);
}
|