Android 二次封装网络加载框架(3)

既然这样,那有没有办法解决呢?

首先,我们先回想一下,网络请求那些参数是必要的,那些是非必要的,即可有可无的。

必要选项

  • 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 实现,采用单例模式

NetManger.getRequest().doGet(url, mMap, new IResponseListener() {

@Override

public void onResponse(String response) {

LogUtil.i(TAG,“onResponse: response =”+response);

}

@Override

public void onFail(HttpException httpException) {

Log.i(TAG, “onFail: httpException=” +httpException.toString());

}

});

2) NetManger 怎么切换具体的实现

加入我们想切换成 Volley,那么我们在传递参数的时候只需要传递VolleyRequest.class 即可

NetManger.getRequest(VolleyRequest.class).doPost(url, mMap, new IResponseListener() {

@Override

public void onResponse(String response) {

mTv.setText(“post 请求\n”+response);

LogUtil.i(TAG,“onResponse: response =”+response);

}

@Override

public void onFail(HttpException httpException) {

Log.i(TAG, “onFail: httpException=” +httpException.toString());

}

});

  1. 如果我们不想使用 okhttp,Volley,而是想使用 XUtils或者 retrofit,有没有办法实现呢?

答案是坑定的,我们只需要自己增加一个实现类 implement NetRequest 接口即可。然后在使用传递参数的时候传递相应的 Class 即可。

NetManger.getRequest(XUtilsRequest.class).doPost(url, mMap, new IResponseListener() {

@Override

public void onResponse(String response) {

mTv.setText(“post 请求\n”+response);

LogUtil.i(TAG,“onResponse: response =”+response);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

面试复习笔记

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960页Android开发笔记》

《1307页Android开发面试宝典》

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

[外链图片转存中…(img-rcPzvBKh-1713731634205)]

面试复习笔记

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960页Android开发笔记》

[外链图片转存中…(img-hRjTdPpo-1713731634207)]

《1307页Android开发面试宝典》

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

[外链图片转存中…(img-1icvj0Ly-1713731634208)]

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-GIWEMM11-1713731634209)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 30
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值