[置顶] 优雅设计封装基于Okhttp3的网络框架(完):原生HttpUrlConnction请求、多线程分发 及 数据转换
2017-08-03 07:57
1121 查看
前5篇博文完成了此框架的一大模块—–多线程下载,而这两篇文章实现另一大模块——Http基本框架封装,在上一篇博文中完成了HttpHeader的接口定义和实现、状态码定义及response、request接口封装和实现,定义了许多接口和抽象类,在接下来编码过程中会体现出程序的扩展性重要性。
在此篇博文中将添加新功能——原生请求的类库支持,你会发现在此基础上只需增加3个类即可,充分体现出了程序的扩展性。新增功能如下:
原生HttpUrlConnction请求和响应
业务层多线程分发处理
移除请求
请求成功类型转换包装处理
(建议阅读此篇文章之前,需理解前两篇文章的讲解,此系列文章是环环相扣,不可缺一,链接如下:)
优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
优雅设计封装基于Okhttp3的网络框架(二):多线程下载功能原理设计 及 简单实现
优雅设计封装基于Okhttp3的网络框架(三):多线程下载功能核心实现 及 线程池、队列机制解析
优雅设计封装基于Okhttp3的网络框架(四):多线程下载添加数据库支持(greenDao)及 进度更新
优雅设计封装基于Okhttp3的网络框架(五):多线程、单例模式优化 及 volatile、构建者模式使用解析
优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
定义成员变量HttpURLConnection及参数HttpMethod、Url来实现Okhttp的请求过程。
提供构造方法初始化以上3个成员变量。
实现抽象方法
实现抽象方法
对header进行处理,循环该参数将所有请求头封装至HttpURLConnection
判断data即传输参数是否为空,写入到HttpURLConnection输出流中。
最后封装完毕,创建原生的响应实现类OriginHttpResponse,将HttpURLConnection传入其构造方法,最后将原生响应实现类OriginHttpResponse返回出去即可。
定义成员变量HttpURLConnection
为此类提供构造方法初始化成员变量
实现接口中的
再提供一些基本方法
实现类内部定义重要成员变量:HttpURLConnection 。
为实现类提供构造方法,参数为HttpURLConnection 。
实现类内部待实现的方法具体编码都依赖于HttpURLConnection成员变量。
代码量虽然不少,主要是实现方法,但是编码简单,查看即可理解,代码如下:
在新增完代码后,最后需要在HttpRequestProvider进行判断调用,这是一个供开发者调用的类
在其构造方法中进行判断更改,可以直接改变网络请求所使用的依赖类库!
在代码实现之前再次强调此网络框架中“队列”的概念,因为在处理多线程请求时,不可能无限制的创建多个线程来处理,而一个队列中存储的是一个Request对象,存储着请求Url、请求方式、数据等相关信息,再提供对应的
内部维护一个线程池成员变量,这里为了能够快速响应多个线程的同时请求数据,将线程数量最大值设置为
提供构造方法初始化成员变量HttpRequestProvider,经过前期封装后,获取请求request对象由专门供上层调用的类HttpRequestProvider完成。
提供
若超过则将request添加cache队列中,等待执行。
若未超过,则通过HttpRequestProvider获取请求request对象,最后由线程池执行。注意,既然是由线程池执行,这里还需要一个Runnable,后续编写。
-提供
成员变量有基本Http封装的接口HttpRequest、多线程请求的接口MultiThreadRequest、管理多线程和队列类WorkStation。
WorkStation主要用于
将HttpRequest中的重要请求数据获取并封装到MultiThreadRequest中来执行请求。
结果显示:
可以看出日志打印,代表POST请求成功,以上代码封装无误。
这里方法的具体实现都是借助开源库Gson来解析数据,比较常用的方法,实现不难,代码如下:
WrapperResponse 继承于MultiThreadResponse,实现其抽象方法
如上,这是未解析响应数据时Api暴露网络请求接口中的实现,其中使用的响应数据是MultiThreadResponse ,在我们封装好可自动解析的数据后,修改使用WrapperResponse ,代码如下:
再次测试,结果正确,以上类型转换封装无误,此网络框架封装完成。
封装基本的网络请求
扩展其对数据库的支持
对多文件上传、多线程文件下载的支持
对Json数据解析等功能的支持
以上是这个网络框架EasyOkhttp封装的全部代码,看似代码量并不少,但是其中定义了大量的接口和抽象类,注重扩展性和解耦性,所以读者可在我封装的基础上继续拓展,根据自身需求添加代码。
如上图所示,此框架可以分为三个层次:
第一层:便于框架扩展,第一层即最底层是Http Interface和Abstact,例如Http中的Headers、Request、Response等通用的原生接口。
第二层:有了第一层请求接口定义,便于第二层对接口的实现,此框架采用两种方式对接口进行实现,分别是Okhttp和原生的HttpURLConnection。通过这两个相关的API去实现整个Http请求和响应的过程,若还想要做相应的拓展,采用别的第三方http请求库,在此处可增加。(已经预先在第一层定义了足够多的接口实现网络请求的回调,第一层可无需修改)对于整个上层业务来说,无需直接接触到底层Okhttp、HttpURLConnection具体实现,所以提供二次封装的 HttpProvider ,暴露接口给上层调用。(具体底层是调用Http还是HttpURLConnection取决于配置,首先判断Okhttp依赖在项目中是否存在,若有则主要采用Okhttp来进行网络请求,否则采用HttpURLConnection)
第三层:即最上层由 Workstation 和Convert组成。Workstation 的中文意思是工作站,用来处理一些线程的调度分发和任务的队列,之所以将它设计在最上层,因为整个多线程、队列机制是与业务层紧密相关的。Convert是为上层开发者提供了更好的接口封装,用于接口返回类型转换、数据解析,例如json、xml等。
文件多线程下载思维导图:
Http设计封装思维导图:
此系列所完成的网络框架封装编码工作暂告一段落,有些功能可能完成的不是很全面,编写此系列过程中收益最大的应当是整体规划封装思想。多线程下载模块多涉及的是Java线程、Http字段有关知识,而后半部分——Http网络框架实现过程中充分体现出了接口、抽象类、实现类这之间的封装思想,而大量的接口和抽象类也体现出整个程序的扩展性、解耦性,这两点从一开始封装网络框架就被视为重点,同时也是我们需要学习的。
此框架可能只算一个简单封装demo,些许部分完成的不是很好,但是这整个封装过程便是精华所在,从一开始的框架架构设计,到功能设计实现、编码优化、bug程序调试等等。这不仅仅只是编码,只涉及到Java单一的内容,同时融合了 Okhttp相关内容、Http协议、接口设计、代码隔离、架构设计、解决思路等综合考虑,此乃重中之重。
编程,或不只是编程。
共勉~
若有错误,虚心指教~
在此篇博文中将添加新功能——原生请求的类库支持,你会发现在此基础上只需增加3个类即可,充分体现出了程序的扩展性。新增功能如下:
原生HttpUrlConnction请求和响应
业务层多线程分发处理
移除请求
请求成功类型转换包装处理
(建议阅读此篇文章之前,需理解前两篇文章的讲解,此系列文章是环环相扣,不可缺一,链接如下:)
优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
优雅设计封装基于Okhttp3的网络框架(二):多线程下载功能原理设计 及 简单实现
优雅设计封装基于Okhttp3的网络框架(三):多线程下载功能核心实现 及 线程池、队列机制解析
优雅设计封装基于Okhttp3的网络框架(四):多线程下载添加数据库支持(greenDao)及 进度更新
优雅设计封装基于Okhttp3的网络框架(五):多线程、单例模式优化 及 volatile、构建者模式使用解析
优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
一. 原生HttpConnection方式请求和响应
以下的封装是为了增强此网络框架的功能扩展性,在除了使用Okhttp方式请求外,在此基础上增加最少的类使网络框架可以支持别的类库请求,例如原生的UrlConnction请求。此时前期所封装的接口扩展性就显得很重要了,所以在上一篇博文中我们定义了大量的接口与抽象类,看似复杂冗余,其实都是在为代码扩展性考虑,而此点中将完成原生请求的封装。1. OriginHttpRequest 原生请求实现类
此类与OkHttpRequest 类相似,都继承于BufferHttpRequest 接口,区别在于一个是原生(HttpURLConnection对象)请求实现类,一个是Okhttp(OkhttpClient对象)请求实现类。所以两者大体实现类似,只是底层执行对象、操作API不同(区别主要体现在executeInternal方法实现上)。组成如下:
定义成员变量HttpURLConnection及参数HttpMethod、Url来实现Okhttp的请求过程。
提供构造方法初始化以上3个成员变量。
实现抽象方法
getMethod()、
getUri()。(这两个抽象方法实现简单,只需返回成员变量即可)
实现抽象方法
executeInternal(HttpHeader header, byte[] data):
对header进行处理,循环该参数将所有请求头封装至HttpURLConnection
判断data即传输参数是否为空,写入到HttpURLConnection输出流中。
最后封装完毕,创建原生的响应实现类OriginHttpResponse,将HttpURLConnection传入其构造方法,最后将原生响应实现类OriginHttpResponse返回出去即可。
/** * @function OriginHttpRequest 原生请求实现类(继承BufferHttpRequest接口) * @author lemon guo */ public class OriginHttpRequest extends BufferHttpRequest { private HttpURLConnection mConnection; private String mUrl; private HttpMethod mMethod; public OriginHttpRequest(HttpURLConnection connection, HttpMethod method, String url) { this.mConnection = connection; this.mUrl = url; this.mMethod = method; } @Override protected HttpResponse executeInternal(HttpHeader header, byte[] data) throws IOException { for (Map.Entry<String, String> entry : header.entrySet()) { mConnection.addRequestProperty(entry.getKey(), entry.getValue()); } mConnection.setDoOutput(true); mConnection.setDoInput(true); mConnection.setRequestMethod(mMethod.name()); mConnection.connect(); if (data != null && data.length > 0) { OutputStream out = mConnection.getOutputStream(); out.write(data,0,data.length); out.close(); } OriginHttpResponse response = new OriginHttpResponse(mConnection); return response; } @Override public HttpMethod getMethod() { return mMethod; } @Override public URI getUri() { return URI.create(mUrl); } }
2. 原生工厂类 OriginHttpRequestFactory
与OkHttpRequest后续设计实现类似,需要在实现类的基础上对HttpRequest对象进行封装,提供方法供上层接口调用,那具体的网络请求是调用HttpURLConnection 来完成。定义成员变量HttpURLConnection
为此类提供构造方法初始化成员变量
实现接口中的
createHttpRequest方法,即创建OriginHttpRequest 对象并返回。
再提供一些基本方法
setConnectionTimeOut设置请求超时时间,
setReadTimeOut、
setWriteTimeOut设置读写时间。(若有其他需求,此处可继续增加)
/** * @function 实现类 OriginHttpRequestFactory(返回HttpRequest对象) * @author lemon guo */ public class OriginHttpRequestFactory implements HttpRequestFactory { private HttpURLConnection mConnection; public OriginHttpRequestFactory() { } public void setReadTimeOut(int readTimeOut) { mConnection.setReadTimeout(readTimeOut); } public void setConnectionTimeOut(int connectionTimeOut) { mConnection.setConnectTimeout(connectionTimeOut); } @Override public HttpRequest createHttpRequest(URI uri, HttpMethod method) throws IOException { mConnection = (HttpURLConnection) uri.toURL().openConnection(); return new OriginHttpRequest(mConnection, method, uri.toString()); } }
3. 原生响应实现类 OriginHttpResponse
相对应的,同Okhttp中的响应实现类OkHttpResponse类似,继承抽象类AbstractHttpResponse,实现父类的方法:实现类内部定义重要成员变量:HttpURLConnection 。
为实现类提供构造方法,参数为HttpURLConnection 。
实现类内部待实现的方法具体编码都依赖于HttpURLConnection成员变量。
代码量虽然不少,主要是实现方法,但是编码简单,查看即可理解,代码如下:
/** * @function 响应实现类 OriginHttpResponse * @author lemon Guo */ public class OriginHttpResponse extends AbstractHttpResponse { private HttpURLConnection mConnection; public OriginHttpResponse(HttpURLConnection connection) { this.mConnection = connection; } @Override public HttpStatus getStatus() { try { return HttpStatus.getValue(mConnection.getResponseCode()); } catch (IOException e) { e.printStackTrace(); } return null; } @Override public String getStatusMsg() { try { return mConnection.getResponseMessage(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override public long getContentLength() { return mConnection.getContentLength(); } @Override protected InputStream getBodyInternal() throws IOException { return mConnection.getInputStream(); } @Override protected void closeInternal() { mConnection.disconnect(); } @Override public HttpHeader getHeaders() { HttpHeader header = new HttpHeader(); for (Map.Entry<String, List<String>> entry : mConnection.getHeaderFields().entrySet()) { header.set(entry.getKey(), entry.getValue().get(0)); } return header; } }
4. 供开发者调用类HttpRequestProvider ☆☆☆☆☆
以上原生请求方式封装完毕后,可以发现总共新增了**OriginHttpRequest、OriginHttpRequestFactory、OriginHttpResponse**3个类而已,这说明此网络框架代码的扩展性还是可行的,在后续想要添加别的请求类库,只要新增此3种代码即可。在新增完代码后,最后需要在HttpRequestProvider进行判断调用,这是一个供开发者调用的类
public HttpRequestProvider() { if (OKHTTP_REQUEST) { mHttpRequestFactory = new OkHttpRequestFactory(); } else { mHttpRequestFactory = new OriginHttpRequestFactory(); } }
在其构造方法中进行判断更改,可以直接改变网络请求所使用的依赖类库!
二. 业务层多线程分发处理
上一点已经完成此网络框架对原生UrlConnction请求的支持,但是还有一个重点没有完成——多线程处理请求,大家都知道在主线程进行网络请求会出现异常,此点就是为了完成异步请求。1. 队列中的请求对象 MultiThreadRequest
在请求队列中需要定义业务层的相关接口,用于上层开发人员调用,上层只关注请求成功success还是失败
fail,对于底层具体试下并不关心。
在代码实现之前再次强调此网络框架中“队列”的概念,因为在处理多线程请求时,不可能无限制的创建多个线程来处理,而一个队列中存储的是一个Request对象,存储着请求Url、请求方式、数据等相关信息,再提供对应的
get、
set方法。
/** * @funtion 业务层多线程分发处理,队列中的Request对象MultiThreadRequest * @author lemon Guo */ public class MultiThreadRequest { private String mUrl; private HttpMethod mMethod; private byte[] mData; private MultiThreadResponse mResponse; private String mContentType; public String getUrl() { return mUrl; } public void setUrl(String url) { mUrl = url; } //相对应的get/set方法 ...... }
2. 响应对象 MultiThreadRequest
根据上一点所讲,上层只关心请求结果成功还是失败,所以响应接口只有以下两个方法。/** * @funtion 响应抽象类MultiThreadResponse * @author lemon Guo */ public abstract class MultiThreadResponse<T> { public abstract void success(MultiThreadRequest request, T data); public abstract void fail(int errorCode, String errorMsg); }
3. 工作站WorkStation
接下来需要一个类来处理多线程中的请求,属于服务的一种,用于处理多线程的控制和队列的管理。内部维护一个线程池成员变量,这里为了能够快速响应多个线程的同时请求数据,将线程数量最大值设置为
Integer.MAX_VALUE。再引入两个队列,一个队列存储着请求request,另一个存储着cache,即待执行的请求request队列(考虑到处理线程数量超过最大限制时)。
提供构造方法初始化成员变量HttpRequestProvider,经过前期封装后,获取请求request对象由专门供上层调用的类HttpRequestProvider完成。
提供
add方法将请求任务添加到队列中。注意在这里需要做一个开启线程最大数判断,例如最多同时开启60个线程处理请求:
若超过则将request添加cache队列中,等待执行。
若未超过,则通过HttpRequestProvider获取请求request对象,最后由线程池执行。注意,既然是由线程池执行,这里还需要一个Runnable,后续编写。
-提供
finish方法,在线程池执行Runnable时,即请求结束会调用此方法,将完成的Request移除队列。
/** * @funtion 业务层多线程分发处理:用于处理多线程的控制和队列的管理 MultiThreadRequest * @author lemon Guo */ public class WorkStation { private static final int MAX_REQUEST_SIZE = 60; private static final ThreadPoolExecutor sThreadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { private AtomicInteger index = new AtomicInteger(); @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("http thread name is " + index.getAndIncrement()); return thread; } }); private Deque<MultiThreadRequest> mRunning = new ArrayDeque<>(); private Deque<MultiThreadRequest> mCache = new ArrayDeque<>(); private HttpRequestProvider mRequestProvider; public WorkStation() { mRequestProvider = new HttpRequestProvider(); } public void add(MultiThreadRequest request) { if (mRunning.size() > MAX_REQUEST_SIZE) { mCache.add(request); } else { doHttpRequest(request); } } public void doHttpRequest(MultiThreadRequest request) { HttpRequest httpRequest = null; try { httpRequest = mRequestProvider.getHttpRequest(URI.create(request.getUrl()), request.getMethod()); } catch (IOException e) { e.printStackTrace(); } sThreadPool.execute(new HttpRunnable(httpRequest, request, this)); } public void finish(MultiThreadRequest request) { mRunning.remove(request); if (mRunning.size() > MAX_REQUEST_SIZE) { return; } if (mCache.size() == 0) { return; } Iterator<MultiThreadRequest> iterator = mCache.iterator(); while (iterator.hasNext()) { MultiThreadRequest next = iterator.next(); mRunning.add(next); iterator.remove(); doHttpRequest(next); } } }
4. HttpRunnable
在专门用于处理多线程的控制和队列的管理类WorkStation中维护了一个线程池,用来执行网络请求,所以需要对应的Runnable来执行下载任务。成员变量有基本Http封装的接口HttpRequest、多线程请求的接口MultiThreadRequest、管理多线程和队列类WorkStation。
WorkStation主要用于
run方法执行完后调用此对象中的方法,来移除队列中已执行完的request。
将HttpRequest中的重要请求数据获取并封装到MultiThreadRequest中来执行请求。
/** * @funtion 业务层多线程分发处理:用于处理下载任务 * @author lemon Guo */ public class HttpRunnable implements Runnable { private HttpRequest mHttpRequest; private MultiThreadRequest mRequest; private WorkStation mWorkStation; public HttpRunnable(HttpRequest httpRequest, MultiThreadRequest request, WorkStation workStation) { this.mHttpRequest = httpRequest; this.mRequest = request; this.mWorkStation = workStation; } @Override public void run() { try { mHttpRequest.getBody().write(mRequest.getData()); HttpResponse response = mHttpRequest.execute(); String contentType = response.getHeaders().getContentType(); mRequest.setContentType(contentType); if (response.getStatus().isSuccess()) { if (mRequest.getResponse() != null) { mRequest.getResponse().success(mRequest, new String(getData(response))); } } } catch (IOException e) { e.printStackTrace(); } finally { mWorkStation.finish(mRequest); } } public byte[] getData(HttpResponse response) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream((int) response.getContentLength()); int len; byte[] data = new byte[512]; try { while ((len = response.getBody().read(data)) != -1) { outputStream.write(data, 0, len); } } catch (IOException e) { e.printStackTrace(); } return outputStream.toByteArray(); } }
5. HttpApiProvider上层调用API
以上代码基本完成,但是为了方便上层调用,需要在此基础上封装一个接口的API,类似之前专门提供Request对象的HttpRequestProvider,此类名为HttpApiProviderpublic class HttpApiProvider { private static final String ENCODING = "utf-8"; private static WorkStation sWorkStation = new WorkStation(); /* * 对请求参数进行编码处理 * */ public static byte[] encodeParam(Map<String, String> value) { if (value == null || value.size() == 0) { return null; } StringBuffer buffer = new StringBuffer(); int count = 0; try { for (Map.Entry<String, String> entry : value.entrySet()) { buffer.append(URLEncoder.encode(entry.getKey(), ENCODING)).append("="). append(URLEncoder.encode(entry.getValue(), ENCODING)); if (count != value.size() - 1) { buffer.append("&"); } count++; } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return buffer.toString().getBytes(); } public static void helloWorld(String ul, Map<String, String> value, MultiThreadResponse response) { MultiThreadRequest request = new MultiThreadRequest(); request.setUrl(ul); request.setMethod(HttpMethod.POST); request.setData(encodeParam(value)); request.setResponse(response); sWorkStation.add(request); } }
6. 测试
以上封装功能已经完成,接下来以一个POST请求来测试这一系列HTTP封装请求,代码如下:Map<String, String> map = new HashMap<>(); map.put("username", "nate"); map.put("userage", "12"); MoocApiProvider.helloWorld("http:....../web/HelloServlet", map, new MoocResponse<Person>() { @Override public void success(MoocRequest request, Person data) { Logger.debug("nate", data.toString()); } @Override public void fail(int errorCode, String errorMsg) { } });
结果显示:
可以看出日志打印,代表POST请求成功,以上代码封装无误。
三. 数据类型自动转换
现在还剩下一个需求需要完善,即响应请求到的数据更加简单直接,是对象类型而并非xml、json数据,所以这涉及到了数据类型转换,为了整个程序的扩展性考虑,首要的还是来封装接口。1. 数据类型转换接口Convert
parse方法中进行类型转换。
isCanParse方法判断此数据是否可以进行转换
/** * @funtion 数据类型转换接口 * @author lemon Guo */ public interface Convert { Object parse(HttpResponse response, Type type) throws IOException; boolean isCanParse(String contentType); Object parse(String content, Type type) throws IOException; }
2. 转换实现类JsonConvert
如上在实现接口之后,可以定义不同类型转换的实现类来实现此接口,此项目中我只定义了JsonConvert,用来进行Json数据转换,相应的,还可以定义XmlConvert实现类等等。这里方法的具体实现都是借助开源库Gson来解析数据,比较常用的方法,实现不难,代码如下:
/** * @funtion 转换实现类JsonConvert * @author lemon Guo */ public class JsonConvert implements Convert { private Gson gson = new Gson(); private static final String CONTENT_TYPE = "application/json;charset=UTF-8"; @Override public Object parse(HttpResponse response, Type type) throws IOException { Reader reader = new InputStreamReader(response.getBody()); return gson.fromJson(reader, type); } @Override public boolean isCanParse(String contentType) { return CONTENT_TYPE.equals(contentType); } @Override public Object parse(String content, Type type) throws IOException { return gson.fromJson(content, type); } }
3. 解析Response数据WrapperResponse
以上封装好类型转换后,需要将此结合到网络请求中,在对MultiThreadResponse做一个上层封装,相当于一层过滤,将获取到的响应数据通过类型转换后再返回。WrapperResponse 继承于MultiThreadResponse,实现其抽象方法
success,在成功响应方法中对数据进行解析类型转换操作。
/** * @funtion WrapperResponse类型转换封装 Response * @author lemon Guo */ public class WrapperResponse extends MultiThreadResponse<String> { private MultiThreadResponse mMoocResponse; private List<Convert> mConvert; public WrapperResponse(MultiThreadResponse moocResponse, List<Convert> converts) { this.mMoocResponse = moocResponse; this.mConvert = converts; } @Override public void success(MultiThreadRequest request, String data) { for (Convert convert : mConvert) { if (convert.isCanParse(request.getContentType())) { try { Object object = convert.parse(data, getType()); mMoocResponse.success(request, object); } catch (IOException e) { e.printStackTrace(); } return; } } } public Type getType() { Type type = mMoocResponse.getClass().getGenericSuperclass(); Type[] paramType = ((ParameterizedType) type).getActualTypeArguments(); return paramType[0]; } @Override public void fail(int errorCode, String errorMsg) { } }
4. 修改API调用类HttpApiProvider
public static void helloWorld(String ul, Map<String, String> value, MultiThreadResponse response) { MultiThreadRequest request = new MultiThreadRequest(); request.setUrl(ul); request.setMethod(HttpMethod.POST); request.setData(encodeParam(value)); request.setResponse(response); sWorkStation.add(request); }
如上,这是未解析响应数据时Api暴露网络请求接口中的实现,其中使用的响应数据是MultiThreadResponse ,在我们封装好可自动解析的数据后,修改使用WrapperResponse ,代码如下:
public static void helloWorld(String ul, Map<String, String> value, MultiThreadResponse response) { MultiThreadRequest request = new MultiThreadRequest(); WrapperResponse wrapperResponse = new WrapperResponse(response, sConverts); request.setUrl(ul); request.setMethod(HttpMethod.POST); request.setData(encodeParam(value)); // request.setResponse(response); request.setResponse(wrapperResponse); sWorkStation.add(request); }
再次测试,结果正确,以上类型转换封装无误,此网络框架封装完成。
四. 网络框架总结
此系列文章旨于:基于okhttp3原始框架来设计封装一个满足业务需求、扩展性强、耦合度低的网络框架。具体框架功能为:封装基本的网络请求
扩展其对数据库的支持
对多文件上传、多线程文件下载的支持
对Json数据解析等功能的支持
1.整体代码
以上是这个网络框架EasyOkhttp封装的全部代码,看似代码量并不少,但是其中定义了大量的接口和抽象类,注重扩展性和解耦性,所以读者可在我封装的基础上继续拓展,根据自身需求添加代码。
2. 架构设计
如上图所示,此框架可以分为三个层次:
第一层:便于框架扩展,第一层即最底层是Http Interface和Abstact,例如Http中的Headers、Request、Response等通用的原生接口。
第二层:有了第一层请求接口定义,便于第二层对接口的实现,此框架采用两种方式对接口进行实现,分别是Okhttp和原生的HttpURLConnection。通过这两个相关的API去实现整个Http请求和响应的过程,若还想要做相应的拓展,采用别的第三方http请求库,在此处可增加。(已经预先在第一层定义了足够多的接口实现网络请求的回调,第一层可无需修改)对于整个上层业务来说,无需直接接触到底层Okhttp、HttpURLConnection具体实现,所以提供二次封装的 HttpProvider ,暴露接口给上层调用。(具体底层是调用Http还是HttpURLConnection取决于配置,首先判断Okhttp依赖在项目中是否存在,若有则主要采用Okhttp来进行网络请求,否则采用HttpURLConnection)
第三层:即最上层由 Workstation 和Convert组成。Workstation 的中文意思是工作站,用来处理一些线程的调度分发和任务的队列,之所以将它设计在最上层,因为整个多线程、队列机制是与业务层紧密相关的。Convert是为上层开发者提供了更好的接口封装,用于接口返回类型转换、数据解析,例如json、xml等。
3. 文件多线程下载和Http设计封装
整个系列的文章可以分成两个部分,即前5篇博文在重点讲解多线程下载有关设计与编码实现,而后两篇博文则是重点讲解Http请求、响应接口封装,两部分的思维导图如下,讲解顺序也是按此进行:文件多线程下载思维导图:
Http设计封装思维导图:
4. 小结
源码此系列所完成的网络框架封装编码工作暂告一段落,有些功能可能完成的不是很全面,编写此系列过程中收益最大的应当是整体规划封装思想。多线程下载模块多涉及的是Java线程、Http字段有关知识,而后半部分——Http网络框架实现过程中充分体现出了接口、抽象类、实现类这之间的封装思想,而大量的接口和抽象类也体现出整个程序的扩展性、解耦性,这两点从一开始封装网络框架就被视为重点,同时也是我们需要学习的。
此框架可能只算一个简单封装demo,些许部分完成的不是很好,但是这整个封装过程便是精华所在,从一开始的框架架构设计,到功能设计实现、编码优化、bug程序调试等等。这不仅仅只是编码,只涉及到Java单一的内容,同时融合了 Okhttp相关内容、Http协议、接口设计、代码隔离、架构设计、解决思路等综合考虑,此乃重中之重。
编程,或不只是编程。
共勉~
若有错误,虚心指教~
相关文章推荐
- 优雅设计封装基于Okhttp3的网络框架(六):HttpHeader接口设计实现 及 Response、Request封装实现
- 优雅设计封装基于Okhttp3的网络框架(一):Http网络协议与Okhttp3解析
- 优雅设计封装基于Okhttp3的网络框架(三):多线程下载功能核心实现 及 线程池、队列机制、终止线程解析
- 优雅设计封装基于Okhttp3的网络框架(五):多线程、单例模式优化 及 volatile、构建者模式使用解析
- 优雅设计封装基于Okhttp3的网络框架(四):多线程下载添加数据库支持(greenDao)及 进度更新
- 优雅设计封装基于Okhttp3的网络框架(二):多线程下载功能原理设计 及 简单实现
- Java&Android开源库代码剖析】のandroid-async-http(如何设计一个优雅的Android网络请求框架,同...
- android http——网络请求二次封装的框架设计
- android http——网络请求二次封装的框架设计
- 基于OkHttp3封装网络请求框架
- fastokhttp是基于okhttp-3.8.1.jar、okio-1.13.0.jar深度封装的OkHttp网络框架解读
- EasyHttp 基于OkHttp的网络请求框架
- 网络层架构设计与实战七框架拓展设计之支持原生的HttpUrlConnection方式请求和响应
- Android基于http封装的网络请求框架
- 使用android-async-http来封装Android网络请求框架
- okhttp-OkGo(okhttp-util升级版)网络请求框架(一)
- okhttp-OkGo(okhttp-util升级版)网络请求框架(二)
- Okhttp3网络请求框架+MVP设计模式简单实战
- 网络请求框架(二)----改善的okHttp封装库okhttputils的使用
- android基于开源网络框架asychhttpclient,二次封装为通用网络请求组件