Volley源码解析

2017-05-23  本文已影响0人  磊少工作室_CTO

导语

阅读源码是已经入门的Android开发者必经之路,这是提高自己对代码理解的一步。但是一开始阅读源码不能深入的去读细节部分,而是理顺源码的主要部分,理解它的实现原理即可,否则一旦陷入会导致很难读懂,可能会失去阅读源码的兴趣。volley现在已经不是主流的网络请求框架,但是其结构并不复杂,容易理解,因此选择此框架来学习。

一、Volley的使用

RequestQueue requestQueue = 
    Volley.newRequestQueue(context.getApplicationContext());// 创建请求队列实例
StringRequest request = new StringRequest(url,
    new Listener<String>() {
        @Override
        public void onResponse(String response) { // 成功返回数据,做UI更新等操作},
    new ErrorListener() {
         @Override
         public void onErrorResponse(VolleyError arg0) {// 失败,做相应处理}
    });
requestQueue.add(request);// 将请求添加至请求队列中

以上是volley常规的使用方式。先是创建请求队列,这个一般在采用单例模式创建然后用于全局;接着创建请求,请求有StringRequest、JsonObjectRequest等等,主要区别就是返回数据格式不同。最后将请求添加至请求队列(循环处理请求)。

二、Volley解析

阅读源码的方法很简单,就是跟进代码,一点点理解。我们从创建请求队列实例开始跟进。(可能会省略部分不重要的源码)

2.1 Volley

跟进Volley.newRequestQueue(context.getApplicationContext()),可以发现此类是volley框架的入口,源码如下:

    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), "volley");

        if(stack == null) {// 在api9以前使用httpClient网络请求,而api9以后使用httpUrlConnection,但是在api23以后已经不支持httpClient了
            if(VERSION.SDK_INT >= 9) {
                stack = new HurlStack();// 用httpUrlConnection请求并获取结果
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));// 用httpClient请求并获取结果
            }
        }

        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);// 处理请求,使用httpStack获得请求结果封装后返回
        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);// 创建请求队列,并传入缓存对象以及处理请求对象
        queue1.start();// 启动了缓存线程以及网络请求线程
        return queue1;
    }

2.2 RequestQueue

我们先跟进queue1.start(),读源码的时候不能一直按顺序读,如果先跟进BasicNetWork或DiskBasedCache会很难理解,但是我们能从语义上基本能看出他们的作用,一个是网络请求一个是磁盘缓存。queue1.start()能看出是队列启动的入口,从这里进入会遇到使用BasicNetWork或DiskBasedCache这些类的时候。

    public void start() {
        this.stop();
        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue,
             this.mNetworkQueue, this.mCache, this.mDelivery);// 这是一个缓存线程,已缓存结果的一些请求不必再去进行网络请求,直接在mCacheQueue里拿结果,同时缓存会有过期、新鲜度等判断,如果不符合条件则取mNetworkQueue里的网络请求。mCache是缓存结果的一个实例,mDelivery是分发结果的实例。
        this.mCacheDispatcher.start();

        for(int i = 0; i < this.mDispatchers.length; ++i) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue,
                this.mNetwork, this.mCache, this.mDelivery);// 网络请求线程,网络请求时间长,因此需要多个线程一起执行,默认是4个,可以自己设置。
            this.mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }

    }

2.3 NetworkDispatcher

先讲解NetworkDispatcher是因为我想要根据一开始执行的流程走, 只有进行过网络请求的才有结果缓存,这样思路比较顺点。NetworkDispatcher是一个处理网络请求的线程。

public void run() {

        while(true) {
            long startTimeMs;
            Request request;
            while(true) {// 只要网络请求队列里出现请求,马上取出执行。
                startTimeMs = SystemClock.elapsedRealtime();
                try {
                    request = (Request)this.mQueue.take();// 从队列拿出请求
                    break;
                } catch (InterruptedException var6) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }

            try {
                request.addMarker("network-queue-take");
                if(request.isCanceled()) {// 这些地方我们就不必去深究,只要语义上理解就好,很明显这是请求取消了,然后请求完成。传入了一些标志的字符串。
                    request.finish("network-discard-cancelled");
                } else {
                    NetworkResponse e = this.mNetwork.performRequest(request);// 这里就是关键的网络请求,拿到封装好的结果(其实是将httpResponse解析成自定义的NetworkResponse)
                    request.addMarker("network-http-complete");
                    if(e.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        Response volleyError1 = request.parseNetworkResponse(e);// 再次将NetWorkResponse对象转换成自定义的Response对象。Response最终可解析成我们需要的数据,而NetworkResonse只是过渡的一个对象。
                        request.addMarker("network-parse-complete");
                        if(request.shouldCache() && volleyError1.cacheEntry != null) {
                            this.mCache.put(request.getCacheKey(), 
                                volleyError1.cacheEntry);// 这里就是缓存结果
                            request.addMarker("network-cache-written");
                        }

                        request.markDelivered();
                        this.mDelivery.postResponse(request, volleyError1);// 这也是关键,将结果分发给request,request再传给监听器回调给主线程
                    }
                }
            } catch (VolleyError var7) {
                var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                this.parseAndDeliverNetworkError(request, var7);
            } catch (Exception var8) {
                VolleyError volleyError = new VolleyError(var8);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                this.mDelivery.postError(request, volleyError);
            }
        }
    }

2.4 BasicNetwork

该类实现了Network接口,主要的作用是进行网络请求并返回结果。 跟进以上代码的mNetwork.performRequest(request)

    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();

        while(true) {
            HttpResponse httpResponse = null;
            Object responseContents = null;
            Map responseHeaders = Collections.emptyMap();

            try {
                HashMap e = new HashMap();
                this.addCacheHeaders(e, request.getCacheEntry());// 取出request中的缓存实体,将部分信息缓存到缓存头中(可能无法理解为什么要有缓存头,不用死缠烂打,先看下面)
                httpResponse = this.mHttpStack.performRequest(request, e);// 这里就是真正的请求数据并返回结果。
                StatusLine statusCode2 = httpResponse.getStatusLine();
                int networkResponse1 = statusCode2.getStatusCode();// 状态码,用于判断服务端的结果是否改变
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());// 解析返回结果的响应头
                if(networkResponse1 != 304) {// 304表示在一定时间内结果是否有改动
                    byte[] responseContents1;
                    if(httpResponse.getEntity() != null) {
                        responseContents1 = this.entityToBytes(httpResponse.getEntity());// 将httpEntry实体转换成字节数组
                    } else {
                        responseContents1 = new byte[0];
                    }

                    long requestLifetime1 = SystemClock.elapsedRealtime() - requestStart;
                    this.logSlowRequests(requestLifetime1, request, responseContents1, statusCode2);
                    if(networkResponse1 >= 200 && networkResponse1 <= 299) {
                        return new NetworkResponse(networkResponse1, responseContents1, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart);// 返回一个根据服务端返回结果来构建的NetworkResponse对象
                    }

                    throw new IOException();
                }

                Entry requestLifetime = request.getCacheEntry();// 执行到这里说明服务端没有改动结果,因此直接取缓存
                if(requestLifetime == null) {
                    return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
                }

                requestLifetime.responseHeaders.putAll(responseHeaders);
                return new NetworkResponse(304, requestLifetime.data, requestLifetime.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
            } catch (SocketTimeoutException var12) {
            }
        }
    }

2.5 HurlStack

很明显,我们要跟进核心部分mHttpStack.performRequest(request, e),这里是通过接口的向上转型,我拿HurlStack实现类来讲解,因为HttpStack已经基本用不到了。

    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap map = new HashMap();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);

        //这部分是参数的拼接,准备用来请求
        URL parsedUrl1 = new URL(url);
        HttpURLConnection connection = this.openConnection(parsedUrl1, request);
        Iterator responseCode = map.keySet().iterator();

        while(responseCode.hasNext()) {
            String protocolVersion = (String)responseCode.next();
            connection.addRequestProperty(protocolVersion, (String)map.get(protocolVersion));
        }

        setConnectionParametersForRequest(connection, request);
        ProtocolVersion protocolVersion1 = new ProtocolVersion("HTTP", 1, 1);
        int responseCode1 = connection.getResponseCode();
        if(responseCode1 == -1) {
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        } else {
            BasicStatusLine responseStatus = new BasicStatusLine(protocolVersion1, connection.getResponseCode(), connection.getResponseMessage());
            BasicHttpResponse response = new BasicHttpResponse(responseStatus);
            response.setEntity(entityFromConnection(connection));// 这句才是真正的网络请求,返回结果后赋予response
            Iterator var12 = connection.getHeaderFields().entrySet().iterator();

            while(var12.hasNext()) {
                Entry header = (Entry)var12.next();
                if(header.getKey() != null) {
                    BasicHeader h = new BasicHeader((String)header.getKey(), (String)((List)header.getValue()).get(0));
                    response.addHeader(h);
                }
            }

            return response;
        }
    }

2.6 ExecutorDelivery

在通过HurlStack获取HttpResponse结果后,我们返回到BasicNetwork继续读代码,发现将HttpResponse转换成NetworkResponse并且返回到NetworkDispatcher,最后执行了mDelivery.postResponse(request, volleyError1)分发结果。

    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        //调用了自定义的Runnable,其实是将子线程切换到了主线程
        this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
    }

    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            this.mRequest = request;
            this.mResponse = response;
            this.mRunnable = runnable;
        }

        public void run() {
            if(this.mRequest.isCanceled()) {// 请求取消
                this.mRequest.finish("canceled-at-delivery");
            } else {
                if(this.mResponse.isSuccess()) {
                    this.mRequest.deliverResponse(this.mResponse.result);// 分发结果给request,request最终分发给监听器
                } else {
                    this.mRequest.deliverError(this.mResponse.error);
                }

            }
        }
    }

2.7 CacheDispatcher

上面已经完成了一个完整的网络请求并且将结果分发给监听器,供开发者使用结果。在之前我们先讲解了NetworkDispatcher,并且也看到了mCache.put(request.getCacheKey(), volleyError1.cacheEntry),将缓存实体存入缓存对象中。CacheDispatcher也是一个线程,循环来取缓存队列里的请求:

    public void run() {
        //省略部分代码...
        this.mCache.initialize();// 缓存类初始化,主要是在SD卡里创建缓存文件夹
        while(true) {
            final Request e = (Request)this.mCacheQueue.take();// 拿出缓存请求
            e.addMarker("cache-queue-take");
            if(e.isCanceled()) {// 请求取消
                e.finish("cache-discard-canceled");
            } else {
                Cache.Entry entry = this.mCache.get(e.getCacheKey());// 根据key拿到缓存实体
                if(entry == null) {
                    e.addMarker("cache-miss");
                    this.mNetworkQueue.put(e);// 如果没有缓存则加入网络请求队列进行网络请求
                } else if(entry.isExpired()) {// 过期的也要加入网络请求队列
                    e.addMarker("cache-hit-expired");
                    e.setCacheEntry(entry);
                    this.mNetworkQueue.put(e);
                } else {
                    e.addMarker("cache-hit");
                    Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));// 将NetworkResponse转换成Response
                    e.addMarker("cache-hit-parsed");
                    if(entry.refreshNeeded()) {// 新鲜度判断,如果需要刷新则在分发结果的同时加入网络请求队列,去获取最新的结果
                        e.addMarker("cache-hit-refresh-needed");
                        e.setCacheEntry(entry);
                        response.intermediate = true;
                        this.mDelivery.postResponse(e, response, new Runnable() {
                            public void run() {
                                try {
                                    CacheDispatcher.this.mNetworkQueue.put(e);
                                } catch (InterruptedException var2) {
                                    ;
                                }

                            }
                        });
                    } else {
                        this.mDelivery.postResponse(e, response);// 分发结果(和NetworkDispatcher一样)
                    }
                }
            }
        }
    }

三、总结

随着一点点的跟进代码,我们理清了Volley的工作流程:
1.初始化网络请求类(BasicNetwork)、缓存类(DiskBasedCache)等,创建请求队列(RequestQueue)并启动缓存请求线程(CacheDispatcher)和网络请求线程(NetworkDispatcher )。
2.CacheDispatcher和NetworkDispatcher不断轮询取出请求,如果有缓存则进行过期时间、新鲜度等判断,来决定是从Cache中取缓存还是再去进行网络请求。网络请求通过BasicNetwork调用HttpStack来返回请求结果后会进行缓存(除非你自己设置不进行缓存)。
3.最后通过ExecutorDelivery实现类分发给我们创建的Request中,而根据我们使用的Request(StringRequest、JsonRequest、ImageRequest等)返回对应的数据结构。
以上就是Volley的主要工作原理,我觉得先看懂主要原理是重要的一步,理解原理后再去看代码细节,比如Volley中的PoolingByteArrayOutputStream(继承ByteArrayOutputStream并给二进制输出流增加缓存区)、HttpHeaderParser(用来解析返回结果的头部)等。

参考:

http://a.codekk.com/detail/Android/grumoon/Volley%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90

上一篇下一篇

猜你喜欢

热点阅读