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 呢,这时候你会怎么写,继续增加参数吗?
这时候接口可能如下:
public interface NetRequest{
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 {
/**
- 网络请求的 TAG
*/
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;
}
}
}
同时,考虑到 NetworkOption 对象的配置会比较复杂,这里我们采用了建造者模式来构建。有兴趣的话,可以参考我的这一篇博客。建造者模式(Builder)及其应用
建造者模式的优点
-
封装性很好,将产品本身与产品的创建过程解耦,对外屏蔽了对象的构建过程
-
扩展性强,如果有新的需求,只需要增加新的具体建造者,无须修改原有类库的代码
NetRequest 接口的封装
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);
}
可以看到,我们主要有几个方法
-
init 方法,主要用来配置一些初始化参数
-
doGet 有两个方法,其中一个方法是另外一个方法的重载,这样设计的目的是为了减少调用方法的时候减少方法参数的传递
-
doPost 跟 doGet 方法一样,就不说了
-
cancel 主要是用来取消网络请求的。在项目当中,在 Activity 或者 Fragment 销毁的时候,最好取消网络请求,不然可能导致内存泄露或者异常,如空指针异常等。
OkHttpRequest 的实现
OkHttp 的配置是非常灵活的,这样我们主要看一下怎么配置请求头,请求参数,以及怎样取消网络请求。
public class OKHttpRequest implements NetRequest {
// ----- 省略若干方法,有兴趣的话上 github 查阅
@Override
public void doGet(String url, Map<String, String> paramsMap, final IResponseListener iResponseListener) {
doGet(url,paramsMap,null,iResponseListener);
}
@Override
public void doGet(String url, Map<String, String> paramsMap, NetworkOption networkOption,
final IResponseListener iResponseListener) {
url= NetUtils.checkUrl(url);
url=NetUtils.appendUrl(url,paramsMap);
final NetworkOption option=NetUtils.checkNetworkOption(networkOption,url);
Request.Builder builder = new Request.Builder().url(url).tag(option.mTag);
builder=configHeaders(builder,option);
Request build = builder.build();
getCilent().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);
}
});
}
private Request.Builder configHeaders(Request.Builder builder, NetworkOption option) {
Map<String, String> headers = option.mHeaders;
if(headers==null || headers.size()==0){
return builder;
}
Set<Map.Entry<String, String>> entries = headers.entrySet();
for(Map.Entry<String, String> entry: entries){
String key = entry.getKey();
String value = entry.getValue();
// 添加请求头
builder.addHeader(key,value);
}
return builder;
}
@Override
public void doPost(String url, Map<String, String> paramsMap, final IResponseListener iResponseListener) {
doPost(url,paramsMap,null,iResponseListener);
}
private FormBody.Builder configPushParam(FormBody.Builder builder, Map<String, String> paramsMap) {
if(paramsMap!=null){
Set<Map.Entry<String, String>> entries = paramsMap.entrySet();
for(Map.Entry<String,String> entry:entries ){
String key = entry.getKey();
String value = entry.getValue();
builder.add(key,value);
}
}
return builder;
}
@Override
public void doPost(String url, Map<String, String> paramsMap, NetworkOption networkOption,
final IResponseListener iResponseListener) {
url= NetUtils.checkUrl(url);
final NetworkOption option=NetUtils.checkNetworkOption(networkOption,url);
// 以表单的形式提交
FormBody.Builder builder = new FormBody.Builder();
builder=configPushParam(builder,paramsMap);
FormBody formBody = builder.build();
Request.Builder requestBuilder = new Request.Builder().url(url).post(formBody).tag(option.mTag);
requestBuilder=configHeaders(requestBuilder,option);
Request request = requestBuilder.build();
getCilent().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);
}
});
}
@Override
public void cancel(Object tag) {
if(client!=null){
if(client != null) {
// 在等待队列中查找是否有相应的请求
for(Call call : client.dispatcher().queuedCalls()) {
if(call.request().tag().equals(tag))
call.cancel();
}
// 在正在请求的请求队列中查找是否有相应的请求
for(Call call : client.dispatcher().runningCalls()) {
if(call.request().tag().equals(tag))
call.cancel();
}
}
}
}
}
OKHttpRequest 的实现其实很就简单,主要是根据 NetworkOption 做相应的配置,不熟悉 okhttpRequest 的用法的可以参考该博客。OkHttp使用完全教程
VolleyRequest
VolleyRequest 的实现也不说了,也是根据 NetworkOption 做相应的配置,有兴趣的话可以点击查看 Networklibrary
NetworkManger
考虑到项目当中有可能要切换框架,这里我们使用简单工厂模式来实现,方便我们框架的随时切换。
UMl 类图如下
-
NetRequest 统一的网络接口
-
VolleyRequest ,Volley 请求网络的具体实现
-
OkhttpRequest,Okhttp 请求网络的实现
-
NetManger ,根据参数的不同返回不同的网络实现
最后考虑到网络加载在项目中是经常用到的,为了节省资源,提高速度,我们结合了单例模式,最终的实现如下:
public class NetManger {
private static NetRequest instance;
private static Context mContext;
public static NetRequest getRequest(){
return instance;
}
static HashMap<String,NetRequest> mMap=new HashMap<>();
public static void init(Context context){
instance = OKHttpRequest.getInstance();
mContext = context.getApplicationContext();
instance.init(NetManger.mContext);
}
// 采用反射的形式实现,这样有一个好处是,以后增加新的实现类的话,我们只需要传递相应 的 Class,
//而不需要更改 NetManger 的代码
public static NetRequest getRequest(Class clz){
String simpleName = clz.getSimpleName();
NetRequest request = mMap.get(simpleName);
if(request ==null){
try {
Constructor constructor = clz.getDeclaredConstructor();
constructor.setAccessible(true);
request = constructor.newInstance();
request.init(mContext);
mMap.put(simpleName,request);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
instance=request;
return request;
}
}
1) NetManger 怎么使用
首先你需要在 Application 中调用 NetManger 的 init 方法
NetManger.init(application);
默认的实现是使用 okhttp 实现,采用单例模式
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
要在 Application 中调用 NetManger 的 init 方法
NetManger.init(application);
默认的实现是使用 okhttp 实现,采用单例模式
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-P2RX07fV-1714939085467)]
[外链图片转存中…(img-1wezR3Ur-1714939085467)]
[外链图片转存中…(img-ydhmwnOr-1714939085467)]
[外链图片转存中…(img-bmqL8heE-1714939085468)]
[外链图片转存中…(img-JScW5R2V-1714939085468)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!