Volley学习及源码分析总结

Volley的简单使用

  • Volley是2013年google退出的网络框架,他的优点是适合进行数据量不大但频繁的通信,对于一些数据量大的网络请求却是表现的非常差
  • 要使用Volley请求网络,我们需要下载到他的jar包,在项目下add
  • 他使用队列来管理网络请求,接下来看一下Volley的简单用法
  • 先创建管理网络请求的队列
RequestQueue queue = Volley.newRequestQueue(appContext);
  • 它提供可使用的请求有好几个,StringRequest,IsonRequest,ImageRequest,等,我们这次就简单的使用一下StringRequest来创建一个网络请求吧
StringRequest stringRequest = new StringRequest(Request.Method.GET, "https://tieba.baidu.com/index.html"
                , new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {
                Log.d(TAG, "onResponse: 请求成功" + s);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.d(TAG, "onErrorResponse: 请求失败");
            }
        });
  • 可以看到,参数分别是请求的方式,url,以及请求成功的回调,和请求失败的回调
  • 然后将请求添加到队列就可以了
queue.add(stringRequest);
  • 简单的使用就到这里,其他的比如Json请求也差不多,这里就不在举例

Volley源码分析

  • 我们就直接按照他的使用步骤来分析
  • 先看创建队列方法,几个构造器最终调用的是三个参数的构造方法
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        File cacheDir = new File(context.getCacheDir(), "volley");
        String userAgent = "volley/0";

        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException var7) {
            ;
        }

//跟据不同的版本创建出不同的栈
        if(stack == null) {
            if(VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork((HttpStack)stack);
        RequestQueue queue;
        //如果设置最大缓存,那么就按照设置的来创建队列,否则就创建默认的队列
        if(maxDiskCacheBytes <= -1) {
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        } else {
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }

        queue.start();
        return queue;
    }
  • 再来看一下queue.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();
        }

    }
  • 好了,队列创建出来了,就该创建我们自己的请求了,然后添加到队列当中,来看一下add方法
public <T> Request<T> add(Request<T> request) {
        //将队列本身与请求相关联
        request.setRequestQueue(this);
        //将这个请求添加到队列的一个hashSet里面
        Set var2 = this.mCurrentRequests;
        synchronized(this.mCurrentRequests) {
            this.mCurrentRequests.add(request);
        }
        //设置一个什么数字
        request.setSequence(this.getSequenceNumber());
        request.addMarker("add-to-queue");
        //查看是否允许缓存,默认为true
        if(!request.shouldCache()) {
        //如果不能缓存,那么将这个请求立即添加到网络队列
            this.mNetworkQueue.add(request);
            return request;
        } else {
        //如果能缓存
            Map var8 = this.mWaitingRequests;
            synchronized(this.mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                //如果正在等待的Map中包含当前请求,就去得到这个请求的队列,这个请求的队列是一个LInkedList
                if(this.mWaitingRequests.containsKey(cacheKey)) {
                    Queue<Request<?>> 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 {
                //如果不包括这个请求,就添加到正在等待的Map
                    this.mWaitingRequests.put(cacheKey, (Object)null);
                    this.mCacheQueue.add(request);
                }

                return request;
            }
        }
    }
  • 先来看看队列的一些本地变量都有些什么
private final Map<String, Queue<Request<?>>> mWaitingRequests;
private final Set<Request<?>> mCurrentRequests;
private final PriorityBlockingQueue<Request<?>> mCacheQueue;
private final PriorityBlockingQueue<Request<?>> mNetworkQueue;
  • 这里我只摘出部分变量,可以看到,有一个正在等待Map,有一个当前请求的set,有一个缓存队列,有一个网络队列
  • 这里再捋一下这个思路,想要将一个请求添加到队列当中
  • 先看看这个请求可不可以缓存,如果不可以缓存,则直接添加到网络队列
  • 如果可以缓存,再看这个请求是否已经包含在正在等待的Map列里面,如果已经包含,那么得到这个请求所对应的队列,对这个队列处理之后,然后将这个请求放在该队列,然后重新将这个队列和这个请求以键值对的方式放在正在等待的map当中
  • 如果不包含在正在等待的map当中的话,直接将这个请求和一个值为空的队列以键值对的方式当在正在等待的Map当中,同时将这个请求放在缓存队列当中
  • 最后,返回这个请求
  • 可见,我们每次add的时候,要么add进网络队列,要么add进正在等待的map,而当add进这个map的时候,总会创建一个对列(LinkedList)与之对应
  • 那么,现在添加进对列了,接下来该怎么办,当然是看我们的缓存线程和网络调度线程的工作啦
  • 先看缓存调度线程的run方法
public void run() {
        if(DEBUG) {
            VolleyLog.v("start new dispatcher", new Object[0]);
        }
//先将线程优先级设置最高,再初始化工作
        Process.setThreadPriority(10);
        this.mCache.initialize();

        while(true) {
            final Request request;
            //第一个死循环,一直循环,直到在缓存队列里拿到请求才跳出这个死循环,
            //因为如果拿不到就会被catch,所以当拿不到请求的时候,除非线程自动停止,否则是走不出循环的
            while(true) {
                request = null;

                try {
                    request = (Request)this.mCacheQueue.take();
                    break;
                } catch (InterruptedException var6) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }

//拿到请求之后先进行一些初始化工作
            try {
                request.addMarker("cache-queue-take");
                if(request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                } else {
                    Entry entry = this.mCache.get(request.getCacheKey());
                    //如果过期或者没得到这个entry,那么就将这个请求放进网络队列
                    if(entry == null) {
                        request.addMarker("cache-miss");
                        this.mNetworkQueue.put(request);
                    } else if(entry.isExpired()) {
                        request.addMarker("cache-hit-expired");
                        request.setCacheEntry(entry);
                        this.mNetworkQueue.put(request);
                    } else {
                        request.addMarker("cache-hit");
                        //这里解析具体数据然后在下面将这些数据返回到主线程
                        Response<?> response = request.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
                        request.addMarker("cache-hit-parsed");
                        //在这里将数据返回到主线程
                        //还要考虑的是如果可以缓存的话,就将数据也放到缓存当中
                        if(!entry.refreshNeeded()) {

                            this.mDelivery.postResponse方法(request, response);
                        } else {
                            request.addMarker("cache-hit-refresh-needed");
                            request.setCacheEntry(entry);
                            response.intermediate = true;
                            this.mDelivery.postResponse(request, response, new Runnable() {
                                public void run() {
                                    try {
                                        CacheDispatcher.this.mNetworkQueue.put(request);
                                    } catch (InterruptedException var2) {
                                        ;
                                    }

                                }
                            });
                        }
                    }
                }
            } catch (Exception var5) {
                VolleyLog.e(var5, "Unhandled exception %s", new Object[]{var5.toString()});
            }
        }
    }
  • 那么现在来看看他是怎么将数据回去主线程的,这里的mDelivery是来自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));
    }
  • 这里的this.mResponsePoster就是当我们创建实例的时候传进去的继承自Handler对象,看一下
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
  • 我们再来看一下这个本身的构造方法
public ExecutorDelivery(final Handler handler) {
        this.mResponsePoster = new Executor() {
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }
  • 看到了没,把Handler传进来之后Handler在实现的Executor接口内部的execute方法中调用了一下handler.post(command);我们再回到postResponse方法
  • 我们的主线程Handler.post方法其实拿到的是这个ExecutorDelivery.ResponseDeliveryRunnable实例参数
  • 这里就是已经用Handler将我们的数据发送到了主线程了
  • 在这里我们看看这个继承自Runable的ExecutorDelivery.ResponseDeliveryRunnable的run方法
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();
                }

            }
        }
  • 这里看到了吧,失败还是成功,在这里回调就好
  • 看完了缓存调度线程,再来看网络调度线程的run方法
public void run() {
        Process.setThreadPriority(10);

        while(true) {
        //刚开始跟缓存调度线程一样
            Request request;
            long startTimeMs;
            while(true) {
                startTimeMs = SystemClock.elapsedRealtime();
                request = null;

                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 {
                    this.addTrafficStatsTag(request);
                    //这里是网络请求的处理逻辑
                    NetworkResponse networkResponse = this.mNetwork.performRequest(request);
                    //在这里已经拿到网络请求完成的数据了
                    request.addMarker("network-http-complete");
                    if(networkResponse.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        Response<?> response = request.parseNetworkResponse(networkResponse);
                        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 var7) {
                var7.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                this.parseAndDeliverNetworkError(request, var7);
            } catch (Exception var8) {
                VolleyLog.e(var8, "Unhandled exception %s", new Object[]{var8.toString()});
                VolleyError volleyError = new VolleyError(var8);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                this.mDelivery.postError(request, volleyError);
            }
        }
    }
  • 这个方法的内部其实跟缓存调度线程是差不多的,这里不再具体分析
  • 这里我们再进去看一下具体的网络请求代码,这个方法this.mNetwork.performRequest(request)
  • 这里的netWork就是我们在创建队列的时候根据不同版本创建的,再来看一下
if(stack == null) {
    if(VERSION.SDK_INT >= 9) {
        stack = new HurlStack();
    } else {
        stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
    }
}

Network network = new BasicNetwork((HttpStack)stack);
  • 根据不同的版本创建不同的请求方式,我们就直接看第一个,因为在BasicNetwork里面调用的也是他的栈参数的方法,看下
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();

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

            try {
                Map<String, String> headers = new HashMap();
                this.addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = this.mHttpStack.performRequest(request, headers);
                ...
            }
        }
    }
  • 方法比较长,这里只截取到开头,我们可以看到他调用的还是参数栈的方法来请求网络,我们继续来看他的栈的这个方法
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError {
        String url = request.getUrl();
        ...

        URL parsedUrl = new URL(url);
        HttpURLConnection connection = this.openConnection(parsedUrl, request);
        Iterator i$ = map.keySet().iterator();

        while(i$.hasNext()) {
            String headerName = (String)i$.next();
            connection.addRequestProperty(headerName, (String)map.get(headerName));
        }

        setConnectionParametersForRequest(connection, request);
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
        int responseCode = connection.getResponseCode();
        if(responseCode == -1) {
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        } else {
            StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage());
            BasicHttpResponse response = new BasicHttpResponse(responseStatus);
            if(hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
                response.setEntity(entityFromConnection(connection));
            }

            Iterator i$ = connection.getHeaderFields().entrySet().iterator();

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

            return response;
        }
    }
  • 在最开始就能看到,这里用的是HttpURLConnection来请求网络的
  • 那么到这里Volley的分析已经完了,其实说起来很简单,就是不停的将请求添加到队列,然后在队列里面有两个线程不停的对这些请求做处理,返回主线程,根据结果回调主线程的方法,这个就是Volley的全部过程了
  • 其实这个Volley也就这样,重点理解他的过程

总结

  • 因为Volley的优点是响应频繁但数据量不大的网络请求,所以他用一个队列来管理这些个请求
  • 他设置了一个缓存队列,这个方式有效的缓解了频繁的网络请求的压力
  • 同时传入主线程Handler,使得最后的传出数据不那么复杂
  • 虽然分缓存调度和网络调度,但是共用的是一套数据

猜你喜欢

转载自blog.csdn.net/asffghfgfghfg1556/article/details/80659351
今日推荐