本文隶属于专题系列: 教你写Android网络框架

前言

在教你写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就可以拥有同一份缓存了。

github地址

SimpleNet网络框架


结束语

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

dexcoder

这家伙太懒了 <( ̄ ﹌  ̄)>
Owner