前言
在教你写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。
package org.simple.net.config;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
/**
* 这是针对于使用HttpUrlStack执行请求时为https请求设置的SSLSocketFactory和HostnameVerifier的配置类,参考
* http://blog.csdn.net/xyz_lmn/article/details/8027334,http://www.cnblogs.com/
* vus520/archive/2012/09/07/2674725.html,
*
* @author mrsimple
*/
public class HttpUrlConnConfig extends HttpConfig {
private static HttpUrlConnConfig sConfig = new HttpUrlConnConfig();
private SSLSocketFactory mSslSocketFactory = null;
private HostnameVerifier mHostnameVerifier = null;
private HttpUrlConnConfig() {
}
public static HttpUrlConnConfig getConfig() {
return sConfig;
}
/**
* 配置https请求的SSLSocketFactory与HostnameVerifier
*
* @param sslSocketFactory
* @param hostnameVerifier
*/
public void setHttpsConfig(SSLSocketFactory sslSocketFactory,
HostnameVerifier hostnameVerifier) {
mSslSocketFactory = sslSocketFactory;
mHostnameVerifier = hostnameVerifier;
}
public HostnameVerifier getHostnameVerifier() {
return mHostnameVerifier;
}
public SSLSocketFactory getSslSocketFactory() {
return mSslSocketFactory;
}
}
在HttpClientStack中执行请求时,会判断是否是https请求,如果是则需要获取配置类中配置的SSLSocketFactory,代码如下 :
/**
* 如果是https请求,则使用用户配置的SSLSocketFactory进行配置.
*
* @param request
*/
private void configHttps(Request<?> request) {
SSLSocketFactory sslSocketFactory = mConfig.getSocketFactory();
if (request.isHttps() && sslSocketFactory != null) {
Scheme sch = new Scheme("https", sslSocketFactory, 443);
mHttpClient.getConnectionManager().getSchemeRegistry().register(sch);
}
}
HttpUrlConnStack的https设置也是类似的,就不给出了。 由于没有服务器,对于Https的配置本人并没有经过测试,如有问题请自行调试了。
Response缓存
在某些情况下,数据并不会每次都需要从服务端获取,因此我们添加了Response缓存。这样就可以避免不必要的请求浪费流量,也可以提升用户体验。用户可以通过Request的setShouldCache(boolean shouldCache)方法来设置是否缓存该请求的Response,如果是true那么则缓存,否则不缓存。
在执行请求时,会判断是否缓存该请求的Response,如果是,那么会将该Response缓存到内存中。如果该请求开启了缓存,那么在请求前会判断是否含有缓存,如果有缓存则直接取缓存结果,没有缓存才从服务端获取。如下是NetworkExecutor中执行网络请求的代码。
@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("", "### 请求分发器退出");
}
}
Response缓存
针对于缓存,我们添加了一个简单的缓存接口。该接口是一个泛型接口,key和value的类型都是泛型。设计为泛型是因为我们在后续的框架中还会使用,后续的ImageLoader框架将以SimpleNet框架为基础来构建一个图片加载框架,其中也会用到缓存接口,但是它的类型却是不一样的,因此我们使用泛型来保证它的扩扩展性。
/**
* 请求缓存接口
*
* @author mrsimple
* @param <K> key的类型
* @param <V> value类型
*/
public interface Cache<K, V> {
public V get(K key);
public void put(K key, V value);
public void remove(K key);
}
针对于网络请求的缓存,我们的实现是LruMemCache类,该类将Response请求结果按照LRU的规则进行缓存。代码如下 :
/**
* 将请求结果缓存到内存中
*
* @author mrsimple
*/
public class LruMemCache implements Cache<String, Response> {
/**
* Reponse缓存
*/
private LruCache<String, Response> mResponseCache;
public LruMemCache() {
// 计算可使用的最大内存
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 取八分之一的可用内存作为缓存
final int cacheSize = maxMemory / 8;
mResponseCache = new LruCache<String, Response>(cacheSize) {
@Override
protected int sizeOf(String key, Response response) {
return response.rawData.length / 1024;
}
};
}
@Override
public Response get(String key) {
return mResponseCache.get(key);
}
@Override
public void put(String key, Response response) {
mResponseCache.put(key, response);
}
@Override
public void remove(String key) {
mResponseCache.remove(key);
}
}
缓存类对象是各个NetworkExecutor共享的,它定义在NetworkExecutor中,如下 :
/**
* 请求缓存
*/
private static Cache<String, Response> mReqCache = new LruMemCache();
这样,多个NetworkExecutor就可以拥有同一份缓存了。