对Volley的源码分析

参考文档

Android Volley完全解析(四),带你从源码的角度理解Volley : http://blog.csdn.net/guolin_blog/article/details/17656437

Volley的工作流程图

如果你对Volley并不了解 , 那么此刻的你看到这张图一定是懵逼的 , 但是没关系 , 等你看完下文后 , 再看这张图一定会很亲切.

开启Volley的奇幻之旅

Volley的三部曲 :

1.创建对应的请求对象(StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest)

2.创建请求队列:RequestQueue queue = Volley.newRequestQueue(context);

3.发起网络请求:queue.add(request)

Request只需要new一个出来即可 , 里面重写了两个回调的方法

    // 解析从网络访问请求的数据
    protected abstract Response<T> parseNetworkResponse(NetworkResponse var1);
    // 将最终解析的数据回调回来,在主线程
    protected abstract void deliverResponse(T var1);

然后 , 我们来看一下Volley的入口点 , RequestQueue queue = Volley.newRequestQueue(context);

    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }

实质上是调用了newRequestQueue()的方法重载,并给第二个参数传入null , 那我们来看这个重载的方法

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

        try {
            String network = context.getPackageName();
            PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
            userAgent = network + "/" + queue.versionCode;
        } catch (NameNotFoundException var6) {
            ;
        }

        if(stack == null) {
            if(VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        queue1.start();
        return queue1;
    }

我们从源码中可以看到 , 第13行 , 判断如果stack是等于null的,则去创建一个HttpStack对象,这里会判断如果手机系统版本号是大于9的,则创建一个HurlStack的实例,否则就创建一个HttpClientStack的实例。实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的.

HttpStack就是一个网络访问的接口 , 选择用哪种方式进行网络请求 , 里面只有一个方法

public interface HttpStack {
    HttpResponse performRequest(Request<?> var1, Map<String, String> var2) throws IOException, AuthFailureError;
}

关于Android访问网络,使用HttpURLConnection还是HttpClient?
我推荐一个文档 : http://blog.csdn.net/guolin_blog/article/details/12452307

那么 , 我们接着看源码 , 创建好了HttpStack之后,接下来又创建了一个Network对象,它是用于根据传入的HttpStack对象来处理网络请求的,紧接着new出一个RequestQueue对象,并调用它的start()方法进行启动,然后将RequestQueue返回,这样newRequestQueue()的方法就执行结束了。

我们看第22行 , new一个RequestQueue对象的同时 , 传入了两个参数 , 第一个参数传入一个DiskBasedCache对象 , 为磁盘缓存对象 , 该方法底层实质上又进行了方法的重载 , 如下 :

    public RequestQueue(Cache cache, Network network) {
        this(cache, network, 4);
    }

可以看到 , 线程池的线程数默认为4条 , 接着重载

    public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

这时 , 创建了一个ExecutorDelivery对象 , 并传入Handler对象 , 做最后请求结果的处理

    public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
        this.mSequenceGenerator = new AtomicInteger();
        this.mWaitingRequests = new HashMap();
        this.mCurrentRequests = new HashSet();
        this.mCacheQueue = new PriorityBlockingQueue();
        this.mNetworkQueue = new PriorityBlockingQueue();
        this.mCache = cache;
        this.mNetwork = network;
        this.mDispatchers = new NetworkDispatcher[threadPoolSize];
        this.mDelivery = delivery;
    }

最终 , 重载为上面的方法 , 第一个参数为缓存接口Cache , 第二个参数为网络请求接口Network , 第三个参数为线程池数threadPoolSize(4个) , 第四个参数为请求结果和错误的分发器接口ResponseDelivery , 处理请求结果

好 , 我们再返回去 , 接下来看请求队列的queue1.start()方法 :

    public void start() {
        this.stop();
        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.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);
            this.mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }

    }

这里先是创建了一个CacheDispatcher的实例,然后调用了它的start()方法,接着在一个for循环里去创建NetworkDispatcher的实例,并分别调用它们的start()方法。这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,而默认情况下for循环会执行四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来(可以调run方法),其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。

接下来 , 就是进行网络请求了 , 调用queue.add(request)方法 , 我们来看一下add()方法里的逻辑

    public Request add(Request request) {
        request.setRequestQueue(this);
        Set var2 = this.mCurrentRequests;
        synchronized(this.mCurrentRequests) {
            this.mCurrentRequests.add(request);
        }

        request.setSequence(this.getSequenceNumber());
        request.addMarker("add-to-queue");
        if(!request.shouldCache()) {
            this.mNetworkQueue.add(request);
            return request;
        } else {
            Map var7 = this.mWaitingRequests;
            synchronized(this.mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                if(this.mWaitingRequests.containsKey(cacheKey)) {
                    Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
                    if(stagedRequests == null) {
                        stagedRequests = new LinkedList();
                    }

                    ((Queue)stagedRequests).add(request);
                    this.mWaitingRequests.put(cacheKey, stagedRequests);
                    if(VolleyLog.DEBUG) {
                        VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
                    }
                } else {
                    this.mWaitingRequests.put(cacheKey, (Object)null);
                    this.mCacheQueue.add(request);
                }

                return request;
            }
        }
    }

可以看到,在第10行的时候 , 会判断当前的请求是否可以缓存,如果不能缓存则在第11行直接将这条请求加入网络请求队列,可以缓存的话则在第30行将这条请求加入缓存队列。在默认情况下,每条请求都是可以缓存的,当然我们也可以调用Request的setShouldCache(false)方法来改变这一默认行为。

OK,那么既然默认每条请求都是可以缓存的,自然就被添加到了缓存队列中,于是一直在后台等待的缓存线程就要开始运行起来了,我们看下CacheDispatcher中的run()方法,代码如下所示:

    public void run() {
        if(DEBUG) {
            VolleyLog.v("start new dispatcher", new Object[0]);
        }

        Process.setThreadPriority(10);
        this.mCache.initialize();

        while(true) {
            while(true) {
                while(true) {
                    while(true) {
                        try {
                            while(true) {
                                final Request e = (Request)this.mCacheQueue.take();
                                e.addMarker("cache-queue-take");
                                if(e.isCanceled()) {
                                    e.finish("cache-discard-canceled");
                                } else {
                                    Entry entry = this.mCache.get(e.getCacheKey());
                                    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));
                                        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);
                                        }
                                    }
                                }
                            }
                        } catch (InterruptedException var4) {
                            if(this.mQuit) {
                                return;
                            }
                        }
                    }
                }
            }
        }
    }

代码有点长,我们只看重点 , 首先在9行可以看到一个while(true)循环,说明缓存线程始终是在运行的,接着在第20行会尝试从缓存当中取出响应结果,如何为空的话则把这条请求加入到网络请求队列中,如果不为空的话 , 第24行再判断该缓存是否已过期,如果已经过期了则同样把这条请求加入到网络请求队列中,否则就认为不需要重发网络请求,直接使用缓存中的数据即可。之后会在第30行调用Request的parseNetworkResponse()方法来对数据进行解析,再往后就是将解析出来的数据进行回调了 , 此处停一下 , mDelivery.postResponse()方法在后面讲解.

因为它的逻辑和NetworkDispatcher后半部分的逻辑是基本相同的,那么我们等下合并在一起看就好了,先来看一下NetworkDispatcher中是怎么处理网络请求队列的,代码如下所示:

    public void run() {
        Process.setThreadPriority(10);

        while(true) {
            Request request;
            while(true) {
                try {
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var4) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }

            try {
                request.addMarker("network-queue-take");
                if(request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                } else {
                    if(VERSION.SDK_INT >= 14) {
                        TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                    }

                    NetworkResponse e = this.mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
                    if(e.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        Response response = request.parseNetworkResponse(e);
                        request.addMarker("network-parse-complete");
                        if(request.shouldCache() && response.cacheEntry != null) {
                            this.mCache.put(request.getCacheKey(), response.cacheEntry);
                            request.addMarker("network-cache-written");
                        }

                        request.markDelivered();
                        this.mDelivery.postResponse(request, response);
                    }
                }
            } catch (VolleyError var5) {
                this.parseAndDeliverNetworkError(request, var5);
            } catch (Exception var6) {
                VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
                this.mDelivery.postError(request, new VolleyError(var6));
            }
        }
    }

同样地,在第6行我们看到了类似的while(true)循环,说明网络请求线程也是在不断运行的。第21行 , 如果该请求没有被取消 , 那么在第26行的时候会调用Network的performRequest()方法来去发送网络请求,而Network是一个接口,这里具体的实现是BasicNetwork,我们来看下它的performRequest()方法 , 进行网络请求,如下所示:

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

        while(true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = (byte[])null;
            HashMap responseHeaders = new HashMap();

            try {
                HashMap e = new HashMap();
                this.addCacheHeaders(e, request.getCacheEntry());
                httpResponse = this.mHttpStack.performRequest(request, e);
                StatusLine statusCode1 = httpResponse.getStatusLine();
                int networkResponse1 = statusCode1.getStatusCode();
                Map responseHeaders1 = convertHeaders(httpResponse.getAllHeaders());
                if(networkResponse1 == 304) {
                    return new NetworkResponse(304, request.getCacheEntry().data, responseHeaders1, true);
                }

                if(httpResponse.getEntity() != null) {
                    responseContents = this.entityToBytes(httpResponse.getEntity());
                } else {
                    responseContents = new byte[0];
                }

                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                this.logSlowRequests(requestLifetime, request, responseContents, statusCode1);
                if(networkResponse1 >= 200 && networkResponse1 <= 299) {
                    return new NetworkResponse(networkResponse1, responseContents, responseHeaders1, false);
                }

                throw new IOException();
            } catch (SocketTimeoutException var12) {
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException var13) {
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException var14) {
                throw new RuntimeException("Bad URL " + request.getUrl(), var14);
            } catch (IOException var15) {
                boolean statusCode = false;
                NetworkResponse networkResponse = null;
                if(httpResponse == null) {
                    throw new NoConnectionError(var15);
                }

                int statusCode2 = httpResponse.getStatusLine().getStatusCode();
                VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode2), request.getUrl()});
                if(responseContents == null) {
                    throw new NetworkError(networkResponse);
                }

                networkResponse = new NetworkResponse(statusCode2, responseContents, responseHeaders, false);
                if(statusCode2 != 401 && statusCode2 != 403) {
                    throw new ServerError(networkResponse);
                }

                attemptRetryOnException("auth", request, new AuthFailureError(networkResponse));
            }
        }
    }

第12行 , 可以看到调用了HttpStack的performRequest()方法 , 这里的HttpStack就是在一开始调用newRequestQueue()方法是创建的实例 , 默认情况下如果系统版本号大于9就创建的HurlStack对象,否则创建HttpClientStack对象。前面已经说过,这两个对象的内部实际就是分别使用HttpURLConnection和HttpClient来发送网络请求的,我们就不再跟进去阅读了,之后会将服务器返回的数据组装成一个NetworkResponse对象进行返回。

走到这里也许头有点晕了吧 , 那么接下来 , 我们要回到NetworkDispatcher的run方法中 , 继续分析 , 拿到mNetwork.performRequest(request)返回的NetworkResponse对象后 , 在31行 , 又会调用request.parseNetworkResponse(e)方法来解析NetworkResponse中的数据,以及将数据写入到缓存,这个方法的实现是交给Request的子类来完成的,因为不同种类的Request解析的方式也肯定不同。

Response response = request.parseNetworkResponse(e);

在解析完了NetworkResponse中的数据之后,在39行 , 又会调用ExecutorDelivery的postResponse()方法来回调解析出的数据,代码如下所示:

    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable));
    }

其中,在mResponsePoster的execute()方法中传入了一个ResponseDeliveryRunnable对象,我们看下run()方法中的代码是什么样的:

    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);
                } else {
                    this.mRequest.deliverError(this.mResponse.error);
                }

                if(this.mResponse.intermediate) {
                    this.mRequest.addMarker("intermediate-response");
                } else {
                    this.mRequest.finish("done");
                }

                if(this.mRunnable != null) {
                    this.mRunnable.run();
                }

            }
        }
    }

其中在第17行调用了Request的deliverResponse()方法,有没有感觉很熟悉?没错,这个就是我们在自定义Request时需要重写的另外一个方法,每一条网络请求的响应都是回调到这个方法中,最后我们再在这个方法中将响应的数据回调到Response.Listener的onResponse()方法中就可以了。

那么 , 问题就来了 , deliverResponse()怎么就是在主线程中执行的呢?我们来看ExecutorDelivery对象是怎么实例化的 :

new ExecutorDelivery(new Handler(Looper.getMainLooper())));

看见Handler对象了吧 , 而且最关键的就是Looper.getMainLooper() , 这是获取主线程的Looper对象 , 它意味着什么我就不在这里讲解了 , 大家不懂的可以看我的对Handler的源码解析 , 这对代码理解影响不大 , 接下来 , 继续看代码

    private final Executor mResponsePoster;

    public ExecutorDelivery(final Handler handler) {
        this.mResponsePoster = new Executor() {
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

这里 , 我们发现原来最终是调用了handler.post()方法 , 回到了主线程.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值