Android网络框架

来源:互联网 发布:淘宝店铺企业认证 编辑:程序博客网 时间:2024/06/07 07:26

从代码学习Android网络框架

代码开源地址: https://github.com/hehonghui/simple_net_framework
作者博客地址: http://blog.csdn.net/column/details/simple-net.html
SimpleNet网络框架 SimpleNet是一个简单的Android网络框架,该框架的结构类似Volley,该框架是为了让不太熟悉框架开发或者说不太了解Android网络编程的同学学习使用。
自己在这里学习一下大神的想法,稍作总结

1.Simple_Net_Framework基本结构

熟悉Volley的同学们发现很相似,没错作者也说了是按照Volley造的轮子。主要分为4部分

1.1 Request

各种请求类型。Request是个抽象,针对不同的请求数据有不同的实现类。例如返回的数据类型为json的对应为JsonRequest,返回数据字符串的为StringRequest,如果需要上传文件,那么你需要使用MultipartRequest,该请求只支持小文件的上传,如果上传的文件过大则会产生OOM

1.2 RequestQueue

第二部分为消息队列,消息队列维护了提交给网络框架的请求列表,并且根据相应的规则进行排序。默认情况下更具优先级和进入队列的顺序来执行,该队列使用的是线程安全的PriorityBlockingQueue,因为我们的队列会被并发的访问,因此需要保证访问的原子性。

1.3 NetWorkExecutors

第三部分是Executor,也就是网络的执行者。该Executor继承自Thread,在run方法中循环访问第二部分的请求队列,请求完成之后将结果投递给UI线程。为了更好的控制请求队列,例如请求排序、取消等操作,这里我们并没有使用线程池来操作,而是自行管理队列和Thread的形式,这样整个结构也变得更为灵活。

1.4 Response Delivery

第四部分则是Response投递类,在第三部分的Executor中执行网络请求,Executor是Thread,但是我们并不能在主线程中更新UI,因此我们使用ResponseDelivery来封装Response的投递,保证Response执行在UI线程。

2.Simple_Net_Framework的设计理论

Request是一个抽象的泛型类,泛型类型就是返回的Response类型,例如StringRequest就是继承自Request。第二部分的RequestQueue依赖于Request,Request是抽象的,因此任何Request的子类都可以传递到请求队列中来,它依赖的是抽象Request,而不是具体的某个实现,因此保证了可扩展性。你可以自己实现自己所需的Request,例如大文件的上传Request。同理,第三部分的NetworkExecutor也只是依赖于Request抽象,但这里又引入了一个类型HttpStack,这个网络请求的真正执行者,有HttpClientStack和HttpUrlConnStack,两者分别为Apache的HttpClient和java的HttpURLConnection,关于这两者的区别请参考:Android访问网络,使用HttpURLConnection还是HttpClient?。HttpStack也是一个抽象,具体使用HttpClient还是HttpURLConnection则由运行系统版本来定,HttpStackFactory会根据系统版本给框架返回对应的HttpStack。最后的ResponseDelivery比较简单了,只是通过Handler将结果投递给UI线程执行,也就是执行RequestListener的onComplete方法,此时网络执行完成,用户即可在该方法中更新UI或者相关的其他的操作。

3.代码阅读

在开始阅读源码前,先看一下如何使用这个Simple_Net_Framework网络框架,这样可以帮我们更好的去入手阅读代码

        //建立一个队列        RequestQueue requestQueue = SimpleNet.newRequestQueue();        //我们的网络请求            StringRequest request = new StringRequest(Request.HttpMethod.GET,                "https://www.baidu.com/", new Request.RequestListener<String>() {            @Override            public void onComplete(int stCode, String response, String errMsg) {                Log.e(TAG, "stCode: "+stCode );                Log.e(TAG, "response: "+response );                Log.e(TAG, "errMsg: "+errMsg );            }        });        //加入队列就ok了,因为队列有钩子函数        requestQueue.addRequest(request);

这里不逐行来分析代码了,根据上面的使用方法来进行分析

3.1 RequestQueue requestQueue = SimpleNet.newRequestQueue()

SimpleNet.java

public final class SimpleNet {    /**     * 创建一个请求队列,NetworkExecutor数量为默认的数量     */    public static RequestQueue newRequestQueue() {        return newRequestQueue(RequestQueue.DEFAULT_CORE_NUMS);    }    /**     * 创建一个请求队列,NetworkExecutor数量为coreNums     */    public static RequestQueue newRequestQueue(int coreNums) {        return newRequestQueue(coreNums, null);    }    /**     * 创建一个请求队列,NetworkExecutor数量为coreNums     * @param coreNums 线程数量     * @param httpStack 网络执行者     */    public static RequestQueue newRequestQueue(int coreNums, HttpStack httpStack) {        RequestQueue queue = new RequestQueue(Math.max(0, coreNums), httpStack);        queue.start();        return queue;    }}

RequestQueue.java

public final class RequestQueue {    /**     * 请求队列 [ Thread-safe ]     */    private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>();    /**     * 请求的序列化生成器     */    private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);    /**     * 默认的核心数     */    public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1;    /**     * CPU核心数 + 1个分发线程数     */    private int mDispatcherNums = DEFAULT_CORE_NUMS;    /**     * NetworkExecutor,执行网络请求的线程     */    private NetworkExecutor[] mDispatchers = null;  //一个Thread,进行一次网络操作    /**     * Http请求的真正执行者     */    private HttpStack mHttpStack;    /**     * @param coreNums 线程核心数     * @param httpStack http执行器     */    protected RequestQueue(int coreNums, HttpStack httpStack) {        mDispatcherNums = coreNums;  //最终开启这么多线程来执行网络操作        mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack(); //网络请求操作的最终执行者,在现在android版本中是对应HttpUrlConnStack ,下面分析    }    /**     * 启动NetworkExecutor     */    private final void startNetworkExecutors() {        mDispatchers = new NetworkExecutor[mDispatcherNums];  //开几个线程        for (int i = 0; i < mDispatcherNums; i++) {            mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack); //一个队列,一个HttpStack执行者,多个线程去执行            mDispatchers[i].start();        }    }    public void start() {        stop();  //防止多次start(),如果mDispatcherNums个线程开启了就先全部关闭        startNetworkExecutors(); //开启mDispatcherNums个线程    }    /**     * 停止NetworkExecutor     */    public void stop() {        if (mDispatchers != null && mDispatchers.length > 0) {            for (int i = 0; i < mDispatchers.length; i++) {                mDispatchers[i].quit();            }        }    }    /**     * 不能重复添加请求     * 很重要的方法,request加入后RequestQueue后就会被开启的多个线程获取request进而执行,     * 所以在使用中只要调用addRequest就完成了网络操作     * @param request     */    public void addRequest(Request<?> request) {           if (!mRequestQueue.contains(request)) {            request.setSerialNumber(this.generateSerialNumber());            mRequestQueue.add(request);        } else {            Log.d("", "### 请求队列中已经含有");        }    }    public void clear() {        mRequestQueue.clear();    }    public BlockingQueue<Request<?>> getAllRequests() {        return mRequestQueue;    }    /**     * 为每个请求生成一个系列号     *      * @return 序列号     */    private int generateSerialNumber() {        return mSerialNumGenerator.incrementAndGet();    }}

RequestQueue是一个重要的核心类,这里总结一下,有一个阻塞队列来存储Request,每次网络请求只需要把new好的Request加入到阻塞队列就好了;还有一个网络请求的真正执行者HttpStack根据版本的不同是HttpClient或HttlUrlConnection(就是这个);多个死循环的线程(线程的数量在new RequestQueue可以控制),每个线程从阻塞队列获取Request然后交个HttpStack来执行网络请求,下面来看这些多线程的代码

NetworkExecutor.java

final class NetworkExecutor extends Thread { //就是一个线程    /**     * 网络请求队列     */    private BlockingQueue<Request<?>> mRequestQueue;    /**     * 网络请求栈,执行者     */    private HttpStack mHttpStack;    /**     * 结果分发器,将网络请求结果投递到主线程,后面分析     */    private static ResponseDelivery mResponseDelivery = new ResponseDelivery();    /**     * 请求缓存,这部分就是使用LruMemCache不做解释     */    private static Cache<String, Response> mReqCache = new LruMemCache();    /**     * 是否停止     */    private boolean isStop = false;    //每个线程拥有RequestQueue中的阻塞队列和网络执行者    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)) {  //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();    }}

对缓存有兴趣可以参考http://blog.csdn.net/chenqianleo/article/details/75137994 ,使用了LruCache,DiskLruCache两级缓存

这部分代码也很简单,在Thread的run()方法获取一个request,使用HttpStack来进行操作然后处理返回结果,这里两个问题,一个是HttpStack是如何处理的?二是如何处理网络请求结果?第一个就是HttpUrlConnect的基本操作,我们下面先来看第二个

ResponseDelivery.java

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); //调用了request的方法,下面来看这个方法            }        };        execute(respRunnable);    }    @Override    public void execute(Runnable command) {        mResponseHandler.post(command);  //这个Runable使用Handler放到了UI线程    }}

Request.java

    public abstract T parseResponse(Response response); //不同的request类型自己处理自己请求的数据    public final void deliveryResponse(Response response) {        T result = parseResponse(response); //返回不同的结果,比如json,string等        if (mRequestListener != null) {            int stCode = response != null ? response.getStatusCode() : -1;            String msg = response != null ? response.getMessage() : "unkown error";            Log.e("", "### 执行回调 : stCode = " + stCode + ", result : " + result + ", err : " + msg);            mRequestListener.onComplete(stCode, result, msg); //回调方法,我们在new一个Request时传入的参数        }    }

阅读到这里大致的流程租完了,我们先创建一个RequestQueue,然后里面会有多个线程等待Request任务的到来,当我们新建一个Request时会传入一个onComplete的钩子函数,最后当我们把Request加入到RequestQueue后就被线程执行得到结果,执行钩子函数。。over