Android网络框架之请求配置与Response缓存(四)

前言

教你写Android网络框架的前三篇文章中,我们从基本结构到代码实现,剖析了一个简单的网络框架应该是怎样运作的,以及在面对各式各样的需求时应该如何对代码做出处理,在深入了解网络框架的同时学习到一些简单的面向对象设计原则。正如第一篇博文所说,SimpleNet框架参照的是Volley实现,甚至有一些类名也是一样的。我们的目标并不是要重新发明轮子,而是以学习轮子制作的过程来达到提升自我的目的。SimpleNet只是一个简单的网络框架实现,没有经过严格的测试以及市场检验,不建议大家在项目中使用,当然如果你觉得没有什么问题,在经过测试的情况下也可以运用在自己的项目中。

请求配置与https

在执行http请求时,我们经常需要对http请求进行配置,例如超时配置和https配置等。SimpleNet在这里只做出了简单的配置,如有更多的需求则请自行实现。由于HttpClient和HttpURLConnection所属的类族是不一样的,他们对于Https的配置并没有一个公共的类型,因此这里没有进行抽象,而是针对两个HttpClient和HttpURLConnection创建来两个配置类,其中HttpClientConfig是HttpClientStack的配置类,而HttpUrlConnConfig则是HttpUrlConnStack的配置类。

例如配置https时,httpClient的SSLSocketFactory所在的包为org.apache.http.conn.ssl.SSLSocketFactory;而HttpURLConnection的SSLSocketFactory所在的包却是javax.net.ssl.SSLSocketFactory。这是apache和Android团队的不同实现,因此不好做出抽象层,我们这里使用两个配置类来进行配置。

使用HttpClient时配置https请参考httpclient中使用HTTPS的方法,使用HttpUrlConnStack执行https请求时配置https请参考Android网络编程——https 不验证证书方式(信任所有证书)

例如,在低于api 9时,用户可以通过HttpClientConfig来配置SSLSocketFactory,然后在执行请求时会获取配置类的SSLSocketFactory来设置HttpClient。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package org.simple.net.config;  
  2.   
  3. import javax.net.ssl.HostnameVerifier;  
  4. import javax.net.ssl.SSLSocketFactory;  
  5.   
  6. /** 
  7.  * 这是针对于使用HttpUrlStack执行请求时为https请求设置的SSLSocketFactory和HostnameVerifier的配置类,参考 
  8.  * http://blog.csdn.net/xyz_lmn/article/details/8027334,http://www.cnblogs.com/ 
  9.  * vus520/archive/2012/09/07/2674725.html, 
  10.  *  
  11.  * @author mrsimple 
  12.  */  
  13. public class HttpUrlConnConfig extends HttpConfig {  
  14.   
  15.     private static HttpUrlConnConfig sConfig = new HttpUrlConnConfig();  
  16.   
  17.     private SSLSocketFactory mSslSocketFactory = null;  
  18.     private HostnameVerifier mHostnameVerifier = null;  
  19.   
  20.     private HttpUrlConnConfig() {  
  21.     }  
  22.   
  23.     public static HttpUrlConnConfig getConfig() {  
  24.         return sConfig;  
  25.     }  
  26.   
  27.     /** 
  28.      * 配置https请求的SSLSocketFactory与HostnameVerifier 
  29.      *  
  30.      * @param sslSocketFactory 
  31.      * @param hostnameVerifier 
  32.      */  
  33.     public void setHttpsConfig(SSLSocketFactory sslSocketFactory,  
  34.             HostnameVerifier hostnameVerifier) {  
  35.         mSslSocketFactory = sslSocketFactory;  
  36.         mHostnameVerifier = hostnameVerifier;  
  37.     }  
  38.   
  39.     public HostnameVerifier getHostnameVerifier() {  
  40.         return mHostnameVerifier;  
  41.     }  
  42.   
  43.     public SSLSocketFactory getSslSocketFactory() {  
  44.         return mSslSocketFactory;  
  45.     }  
  46.   
  47. }  

在HttpClientStack中执行请求时,会判断是否是https请求,如果是则需要获取配置类中配置的SSLSocketFactory,代码如下 : 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 如果是https请求,则使用用户配置的SSLSocketFactory进行配置. 
  3.  *  
  4.  * @param request 
  5.  */  
  6. private void configHttps(Request<?> request) {  
  7.     SSLSocketFactory sslSocketFactory = mConfig.getSocketFactory();  
  8.     if (request.isHttps() && sslSocketFactory != null) {  
  9.         Scheme sch = new Scheme("https", sslSocketFactory, 443);  
  10.         mHttpClient.getConnectionManager().getSchemeRegistry().register(sch);  
  11.     }  
  12. }  

HttpUrlConnStack的https设置也是类似的,就不给出了。 由于没有服务器,对于Https的配置本人并没有经过测试,如有问题请自行调试了。


Response缓存

在某些情况下,数据并不会每次都需要从服务端获取,因此我们添加了Response缓存。这样就可以避免不必要的请求浪费流量,也可以提升用户体验。用户可以通过Request的setShouldCache(boolean shouldCache)方法来设置是否缓存该请求的Response,如果是true那么则缓存,否则不缓存。

在执行请求时,会判断是否缓存该请求的Response,如果是,那么会将该Response缓存到内存中。如果该请求开启了缓存,那么在请求前会判断是否含有缓存,如果有缓存则直接取缓存结果,没有缓存才从服务端获取。如下是NetworkExecutor中执行网络请求的代码。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public void run() {  
  3.     try {  
  4.         while (!isStop) {  
  5.             final Request<?> request = mRequestQueue.take();  
  6.             if (request.isCanceled()) {  
  7.                 Log.d("### ""### 取消执行了");  
  8.                 continue;  
  9.             }  
  10.             Response response = null;  
  11.             if (isUseCache(request)) {  
  12.                 // 从缓存中取  
  13.                 response = mReqCache.get(request.getUrl());  
  14.             } else {  
  15.                 // 从网络上获取数据  
  16.                 response = mHttpStack.performRequest(request);  
  17.                 // 如果该请求需要缓存,那么请求成功则缓存到mResponseCache中  
  18.                 if (request.shouldCache() && isSuccess(response)) {  
  19.                     mReqCache.put(request.getUrl(), response);  
  20.                 }  
  21.             }  
  22.   
  23.             // 分发请求结果  
  24.             mResponseDelivery.deliveryResponse(request, response);  
  25.         }  
  26.     } catch (InterruptedException e) {  
  27.         Log.i("""### 请求分发器退出");  
  28.     }  
  29.   
  30. }  

Response缓存

针对于缓存,我们添加了一个简单的缓存接口。该接口是一个泛型接口,key和value的类型都是泛型。设计为泛型是因为我们在后续的框架中还会使用,后续的ImageLoader框架将以SimpleNet框架为基础来构建一个图片加载框架,其中也会用到缓存接口,但是它的类型却是不一样的,因此我们使用泛型来保证它的扩扩展性。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 请求缓存接口 
  3.  *  
  4.  * @author mrsimple 
  5.  * @param <K> key的类型 
  6.  * @param <V> value类型 
  7.  */  
  8. public interface Cache<K, V> {  
  9.   
  10.     public V get(K key);  
  11.   
  12.     public void put(K key, V value);  
  13.   
  14.     public void remove(K key);  
  15.   
  16. }  

针对于网络请求的缓存,我们的实现是LruMemCache类,该类将Response请求结果按照LRU的规则进行缓存。代码如下 : 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 将请求结果缓存到内存中 
  3.  *  
  4.  * @author mrsimple 
  5.  */  
  6. public class LruMemCache implements Cache<String, Response> {  
  7.   
  8.     /** 
  9.      * Reponse缓存 
  10.      */  
  11.     private LruCache<String, Response> mResponseCache;  
  12.   
  13.     public LruMemCache() {  
  14.         // 计算可使用的最大内存  
  15.         final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  16.   
  17.         // 取八分之一的可用内存作为缓存  
  18.         final int cacheSize = maxMemory / 8;  
  19.         mResponseCache = new LruCache<String, Response>(cacheSize) {  
  20.   
  21.             @Override  
  22.             protected int sizeOf(String key, Response response) {  
  23.                 return response.rawData.length / 1024;  
  24.             }  
  25.         };  
  26.   
  27.     }  
  28.   
  29.     @Override  
  30.     public Response get(String key) {  
  31.         return mResponseCache.get(key);  
  32.     }  
  33.   
  34.     @Override  
  35.     public void put(String key, Response response) {  
  36.         mResponseCache.put(key, response);  
  37.     }  
  38.   
  39.     @Override  
  40.     public void remove(String key) {  
  41.         mResponseCache.remove(key);  
  42.     }  
  43. }  

缓存类对象是各个NetworkExecutor共享的,它定义在NetworkExecutor中,如下 : 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 请求缓存 
  3.  */  
  4. private static Cache<String, Response> mReqCache = new LruMemCache();  

这样,多个NetworkExecutor就可以拥有同一份缓存了。


github地址

SimpleNet网络框架


结束语

Android网络框架到此就已经结束了,由于公司项目原因,很多东西写得比较随意,但不管写得好与烂,总归是一份善意的分享。正如上文所说,我们的目的并不是重新发明轮子,而是在学习发明轮子的过程中提升自己的技术能力、设计能力、抽象能力。或者说我们在学习实现这个网络框架的过程中能够让自己有所领悟,能够对“面向接口编程,而不是面向实现编程”这句话的含义有所理解。正如郭林所说的,写代码到了一定阶段,更重要的是要读代码。从优秀的代码中学习优秀的设计与实现,这是从码农到工程师的一个必经阶段。
当你能够体会到软件之美时,说明你已经到了一个新的阶段。你能够体会到设计之美、架构之美,在你的眼中优秀的设计是美的,而不只是一堆乱七八糟却能实现功能的代码,你能够在其中得到满足。每个人都是从菜鸟慢慢进步而来的,什么事都不是一蹴而就。本人写这一系列的博文,只是希望在自我提升的同时也能够帮助到一些需要帮助的同行,虽然本人水平很有限,但是能将自己的体会分享给别人,甚至说是帮到他人,那就是这个系列的初衷了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值