教你写Android网络框架之Http请求的分发与执行
2015-07-11 16:42
579 查看
教你写Android网络框架之Http请求的分发与执行
前言
在前两篇( 教你写Android网络框架之基本架构、教你写Android网络框架之Request、Response类与请求队列 )博客中,我们已经介绍了SimpleNet框架的基本结构,以及Request、Response、请求队列的实现,以及为什么要这么设计,这么设计的考虑是什么。前两篇博客中已经介绍了各个角色,今天我们就来剖析另外几个特别重要的角色,即NetworkExecutor、HttpStack以及ResponseDelivery,它们分别对应的功能是网络请求线程、Http执行器、Response分发,这三者是执行http请求和处理Response的核心。我们再来回顾一下,SimpleNet各个角色的分工合作。首先用户需要创建一个请求队列,然后将各个请求添加到请求队列中。多个NetworkExecutor ( 实质上是一个线程 )共享一个消息队列,在各个NetworkExecutor中循环的取请求队列中的请求,拿到一个请求,然后通过HttpStack来执行Http请求,请求完成后最终通过ResponseDelivery将Response结果分发到UI线程,保证请求回调执行在UI线程,这样用户就可以直接在回调中更新UI。执行流程如图1.
图1
还有不太了解这幅架构图的可以参考专栏中的第一篇博客。
NetworkExecutor
作为SimpleNet中的“心脏”,NetworkExecutor起着非常重要的作用。之所以称之为“心脏”,是由于NetworkExecutor的功能是源源不断地从请求队列中获取请求,然后交给HttpStack来执行。它就像汽车中的发动机,人体中的心脏一样,带动着整个框架的运行。NetworkExecutor实质上是一个Thread,在run方法中我们会执行一个循环,不断地从请求队列中取得请求,然后交给HttpStack,由于比较简单我们直接上代码吧。
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 | /** * 网络请求Executor,继承自Thread,从网络请求队列中循环读取请求并且执行 * * @author mrsimple */ final class NetworkExecutor extends Thread { /** * 网络请求队列 */ private BlockingQueue<Request<?>> mRequestQueue; /** * 网络请求栈 */ private HttpStack mHttpStack; /** * 结果分发器,将结果投递到主线程 */ private static ResponseDelivery mResponseDelivery = new ResponseDelivery(); /** * 请求缓存 */ private static Cache<String, Response> mReqCache = new LruMemCache(); /** * 是否停止 */ private boolean isStop = false; public NetworkExecutor(BlockingQueue<Request<?>> queue, HttpStack httpStack) { mRequestQueue = queue; mHttpStack = httpStack; } @Override public void run() { try { while (!isStop) { final Request<?> request = mRequestQueue.take(); if (request.isCanceled()) { Log.d("### ", "### 取消执行了"); continue; } Response response = null; if (isUseCache(request)) { // 从缓存中取 response = mReqCache.get(request.getUrl()); } else { // 从网络上获取数据 response = mHttpStack.performRequest(request); // 如果该请求需要缓存,那么请求成功则缓存到mResponseCache中 if (request.shouldCache() && isSuccess(response)) { mReqCache.put(request.getUrl(), response); } } // 分发请求结果 mResponseDelivery.deliveryResponse(request, response); } } catch (InterruptedException e) { Log.i("", "### 请求分发器退出"); } } private boolean isSuccess(Response response) { return response != null && response.getStatusCode() == 200; } private boolean isUseCache(Request<?> request) { return request.shouldCache() && mReqCache.get(request.getUrl()) != null; } public void quit() { isStop = true; interrupt(); } } |
HttpStack
HttpStack只是一个接口,只有一个performRequest函数,也就是执行请求。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** * 执行网络请求的接口 * * @author mrsimple */ public interface HttpStack { /** * 执行Http请求 * * @param request 待执行的请求 * @return */ public Response performRequest(Request<?> request); } |
例如 :
123456789101112131415161718192021222324252627282930313233343536 | /** * @param coreNums 线程核心数 * @param httpStack http执行器 */ protected RequestQueue(int coreNums, HttpStack httpStack) { mDispatcherNums = coreNums; mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack(); } 在购置请求队列时会传递HttpStack,如果httpStack为空,则由HttpStackFactory根据api版本生成对应的HttpStack。即api 9以下是HttpClientStack, api 9 及其以上则为HttpUrlConnStack。[java] view plaincopy在CODE上查看代码片派生到我的代码片/** * 根据api版本选择HttpClient或者HttpURLConnection * * @author mrsimple */ public final class HttpStackFactory { private static final int GINGERBREAD_SDK_NUM = 9; /** * 根据SDK版本号来创建不同的Http执行器,即SDK 9之前使用HttpClient,之后则使用HttlUrlConnection, * 两者之间的差别请参考 : * http://android-developers.blogspot.com/2011/09/androids-http-clients.html * * @return */ public static HttpStack createHttpStack() { int runtimeSDKApi = Build.VERSION.SDK_INT; if (runtimeSDKApi >= GINGERBREAD_SDK_NUM) { return new HttpUrlConnStack(); } return new HttpClientStack(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /** * 使用HttpURLConnection执行网络请求的HttpStack * * @author mrsimple */ public class HttpUrlConnStack implements HttpStack { /** * 配置Https */ HttpUrlConnConfig mConfig = HttpUrlConnConfig.getConfig(); @Override public Response performRequest(Request<?> request) { HttpURLConnection urlConnection = null; try { // 构建HttpURLConnection urlConnection = createUrlConnection(request.getUrl()); // 设置headers setRequestHeaders(urlConnection, request); // 设置Body参数 setRequestParams(urlConnection, request); // https 配置 configHttps(request); return fetchResponse(urlConnection); } catch (Exception e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } } return null; } private HttpURLConnection createUrlConnection(String url) throws IOException { URL newURL = new URL(url); URLConnection urlConnection = newURL.openConnection(); urlConnection.setConnectTimeout(mConfig.connTimeOut); urlConnection.setReadTimeout(mConfig.soTimeOut); urlConnection.setDoInput(true); urlConnection.setUseCaches(false); return (HttpURLConnection) urlConnection; } private void configHttps(Request<?> request) { if (request.isHttps()) { SSLSocketFactory sslFactory = mConfig.getSslSocketFactory(); // 配置https if (sslFactory != null) { HttpsURLConnection.setDefaultSSLSocketFactory(sslFactory); HttpsURLConnection.setDefaultHostnameVerifier(mConfig.getHostnameVerifier()); } } } private void setRequestHeaders(HttpURLConnection connection, Request<?> request) { Set<String> headersKeys = request.getHeaders().keySet(); for (String headerName : headersKeys) { connection.addRequestProperty(headerName, request.getHeaders().get(headerName)); } } protected void setRequestParams(HttpURLConnection connection, Request<?> request) throws ProtocolException, IOException { HttpMethod method = request.getHttpMethod(); connection.setRequestMethod(method.toString()); // add params byte[] body = request.getBody(); if (body != null) { // enable output connection.setDoOutput(true); // set content type connection .addRequestProperty(Request.HEADER_CONTENT_TYPE, request.getBodyContentType()); // write params data to connection DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); dataOutputStream.write(body); dataOutputStream.close(); } } private Response fetchResponse(HttpURLConnection connection) throws IOException { // Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); int responseCode = connection.getResponseCode(); if (responseCode == -1) { throw new IOException("Could not retrieve response code from HttpUrlConnection."); } // 状态行数据 StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); // 构建response Response response = new Response(responseStatus); // 设置response数据 response.setEntity(entityFromURLConnwction(connection)); addHeadersToResponse(response, connection); return response; } /** * 执行HTTP请求之后获取到其数据流,即返回请求结果的流 * * @param connection * @return */ private HttpEntity entityFromURLConnwction(HttpURLConnection connection) { BasicHttpEntity entity = new BasicHttpEntity(); InputStream inputStream = null; try { inputStream = connection.getInputStream(); } catch (IOException e) { e.printStackTrace(); inputStream = connection.getErrorStream(); } // TODO : GZIP entity.setContent(inputStream); entity.setContentLength(connection.getContentLength()); entity.setContentEncoding(connection.getContentEncoding()); entity.setContentType(connection.getContentType()); return entity; } private void addHeadersToResponse(BasicHttpResponse response, HttpURLConnection connection) { for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); response.addHeader(h); } } } } |
ResponseDelivery
在HttpStack的performRequest函数中,我们会返回一个Response对象,该对象包含了我们请求对应的Response。关于Response类你不太了解的可以参考教你写Android网络框架之Request、Response类与请求队列。我们在NetworkExecutor中执行http请求的最后一步会将结果分发给UI线程,主要工作其实就是将请求的回调执行到UI线程,以便用户可以更新UI等操作。12345678910111213141516171819202122232425262728293031 | @Override public void run() { try { while (!isStop) { final Request<?> request = mRequestQueue.take(); if (request.isCanceled()) { Log.d("### ", "### 取消执行了"); continue; } Response response = null; if (isUseCache(request)) { // 从缓存中取 response = mReqCache.get(request.getUrl()); } else { // 从网络上获取数据 response = mHttpStack.performRequest(request); // 如果该请求需要缓存,那么请求成功则缓存到mResponseCache中 if (request.shouldCache() && isSuccess(response)) { mReqCache.put(request.getUrl(), response); } } // 分发请求结果 mResponseDelivery.deliveryResponse(request, response); } } catch (InterruptedException e) { Log.i("", "### 请求分发器退出"); } } |
这其中主要就是抽象和泛型,写框架很多时候泛型是很重要的手段,因此熟悉使用抽象和泛型是面向对象开发的重要一步。
ResponseDelivery代码如下 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | /** * 请求结果投递类,将请求结果投递给UI线程 * * @author mrsimple */ class ResponseDelivery implements Executor { /** * 主线程的hander */ Handler mResponseHandler = new Handler(Looper.getMainLooper()); /** * 处理请求结果,将其执行在UI线程 * * @param request * @param response */ public void deliveryResponse(final Request<?> request, final Response response) { Runnable respRunnable = new Runnable() { @Override public void run() { request.deliveryResponse(response); } }; execute(respRunnable); } @Override public void execute(Runnable command) { mResponseHandler.post(command); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * 处理Response,该方法运行在UI线程. * * @param response */ public final void deliveryResponse(Response response) { T result = parseResponse(response); if (mRequestListener != null) { int stCode = response != null ? response.getStatusCode() : -1; String msg = response != null ? response.getMessage() : "unkown error"; mRequestListener.onComplete(stCode, result, msg); } } |
不同用户的服务器返回的数据格式是不一致的,因此我们定义了Request泛型基类,泛型T就是返回的数据格式类型。比如返回的数据格式为json,那对应的请求就是JsonRequest,泛型T为JSONObject,在JsonRequest中覆写parseResponse函数,将得到的Response中的原始数据转换成JSONObject。然后将请求放到队列中,NetworkExecutor将请求分发给HttpStack执行,执行完成之后得到Response对象,最终ResponseDelivery将结果通过请求回调投递到UI线程。
相关文章推荐
- 教你写Android网络框架之基本架构
- NS3网络仿真(3): NetAnim
- 使用InternetGetConnectedState这个API判断网络是否连通
- 90社交网络的行为报告后:不拒绝陌生人,TFBOYS作为一个喜爱
- 配置Linux连接网络存储
- 最值得一看的几条简单的谷歌 Google 搜索技巧,瞬间提升你的网络搜索能力!
- 关于Tomcat的点点滴滴(体系架构、处理http请求的过程、安装和配置、目录结构、设置压缩和对中文文件名的支持、以及Catalina这个名字的由来……等)
- HttpServletResponse
- 使用Git上传本地项目到http://git.oschina.net
- CentOS6.8 x64+Nginx1.3.8/Apache-httpd 2.4.3+PHP5.4.8(php-fpm)+MySQL5.5.28+CoreSeek4.1源码编译安装
- SpringMVC源码剖析(五)-消息转换器HttpMessageConverter
- HTTP 状态码详解
- TCP连接与释放 浅析
- HTTP 笔记与总结(3 )socket 编程:发送 GET 请求
- 如何理解HTTP协议的 “无连接,无状态” 特点
- 分享非常有用的Java程序(关键代码)(八)---Java InputStream读取网络响应Response数据的方法!(重要)
- JavaWeb学习笔记——开发动态WEB资源(八)cookies和httpsession
- Android_开源框架_AndroidUniversalImageLoader网络图片加载
- 开源框架Android-Query 实现https自签名ssl证书验证
- 十个主要的网络密码破解方法