美文网首页
Volley源码解析

Volley源码解析

作者: 磊少工作室_CTO | 来源:发表于2017-05-23 15:37 被阅读0次

    导语

    阅读源码是已经入门的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

    相关文章

      网友评论

          本文标题:Volley源码解析

          本文链接:https://www.haomeiwen.com/subject/qjpbxxtx.html