Android网络框架

从代码学习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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值