美文网首页
Volley框架源码分析

Volley框架源码分析

作者: 懒癌患者2018 | 来源:发表于2017-07-10 18:54 被阅读0次

    1.Volley框架的使用

    RequestQueue queue = Volley.newRequestQueue(this);
            StringRequest request = new StringRequest("http://www.baidu.com", new com.android.volley.Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
    
                }
            }, new com.android.volley.Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
    
                }
            });
            queue.add(request);
    

    使用步骤非常简单,先初始化一个请求队列,然后构建一个请求对象,再将请求对象加入到请求队列中。

    2.Volley执行流程

    先看下创建请求队列的方法

    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
            File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
    
            String userAgent = "volley/0";
            try {
                String packageName = context.getPackageName();
                PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
                userAgent = packageName + "/" + info.versionCode;
            } catch (NameNotFoundException e) {
            }
    
            if (stack == null) {
                if (Build.VERSION.SDK_INT >= 9) {
                    stack = new HurlStack();
                } else {
                    // Prior to Gingerbread, HttpUrlConnection was unreliable.
                    // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                    stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
                }
            }
    
            Network network = new BasicNetwork(stack);
            
            RequestQueue queue;
            if (maxDiskCacheBytes <= -1)
            {
                // No maximum size specified
                queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
            }
            else
            {
                // Disk cache size specified
                queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
            }
    
            queue.start();
    
            return queue;
        }
    

    这个方法的作用:

    1. 先试图去创建一个目录
    2. 根据SDK的版本号创建Network对象,如果SDK大于9,底层使用HttpURLConnection,反之,将使用HttpClient
    3. 基于创建的Network对象生成RequestQueue对象,并调用start方法,返回对象。

    然后来看下RequestQueue的start方法:

    public void start() {
            stop();  // Make sure any currently running dispatchers are stopped.
            // Create the cache dispatcher and start it.
            mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
            mCacheDispatcher.start();
    
            // Create network dispatchers (and corresponding threads) up to the pool size.
            for (int i = 0; i < mDispatchers.length; i++) {
                NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                        mCache, mDelivery);
                mDispatchers[i] = networkDispatcher;
                networkDispatcher.start();
            }
        }
    

    1.如果当前volley已经在工作了,会先停止工作
    2.创建CacheDispather并开启,这是一个缓存分发线程类。
    3.根据线程池的数量创建目标树木的NetworkDispatcher并开启,这是一个网络请求分发线程类。

    然后来看下RequestQueue的add方法:

    public <T> Request<T> add(Request<T> request) {
            // Tag the request as belonging to this queue and add it to the set of current requests.
            request.setRequestQueue(this);
            synchronized (mCurrentRequests) {
                mCurrentRequests.add(request);
            }
    
            // Process requests in the order they are added.
            request.setSequence(getSequenceNumber());
            request.addMarker("add-to-queue");
    
            // If the request is uncacheable, skip the cache queue and go straight to the network.
            if (!request.shouldCache()) {
                mNetworkQueue.add(request);
                return request;
            }
    
            // Insert request into stage if there's already a request with the same cache key in flight.
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                if (mWaitingRequests.containsKey(cacheKey)) {
                    // There is already a request in flight. Queue up.
                    Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                    if (stagedRequests == null) {
                        stagedRequests = new LinkedList<Request<?>>();
                    }
                    stagedRequests.add(request);
                    mWaitingRequests.put(cacheKey, stagedRequests);
                    if (VolleyLog.DEBUG) {
                        VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                    }
                } else {
                    // Insert 'null' queue for this cacheKey, indicating there is now a request in
                    // flight.
                    mWaitingRequests.put(cacheKey, null);
                    mCacheQueue.add(request);
                }
                return request;
            }
        }
    
    1. 先将请求加入集合并标记
    2. 判断请求是否可以缓存,不可以的话就直接加入网络请求队列
    3. 如果可以缓存,就根据request的缓存key去查找等待请求队列中是否有这个请求
    4. 如果这是新请求就加入等待请求集合并加入缓存队列中

    最后我们来看下,最为主要的两个类的run方法,先看CacheDispatcher的run方法:

     @Override
        public void run() {
            if (DEBUG) VolleyLog.v("start new dispatcher");
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    
            // Make a blocking call to initialize the cache.
            mCache.initialize();
    
            Request<?> request;
            while (true) {
                // release previous request object to avoid leaking request object when mQueue is drained.
                request = null;
                try {
                    // Take a request from the queue.
                    request = mCacheQueue.take();
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
                try {
                    request.addMarker("cache-queue-take");
    
                    // If the request has been canceled, don't bother dispatching it.
                    if (request.isCanceled()) {
                        request.finish("cache-discard-canceled");
                        continue;
                    }
    
                    // Attempt to retrieve this item from cache.
                    Cache.Entry entry = mCache.get(request.getCacheKey());
                    if (entry == null) {
                        request.addMarker("cache-miss");
                        // Cache miss; send off to the network dispatcher.
                        mNetworkQueue.put(request);
                        continue;
                    }
    
                    // If it is completely expired, just send it to the network.
                    if (entry.isExpired()) {
                        request.addMarker("cache-hit-expired");
                        request.setCacheEntry(entry);
                        mNetworkQueue.put(request);
                        continue;
                    }
    
                    // We have a cache hit; parse its data for delivery back to the request.
                    request.addMarker("cache-hit");
                    Response<?> response = request.parseNetworkResponse(
                            new NetworkResponse(entry.data, entry.responseHeaders));
                    request.addMarker("cache-hit-parsed");
    
                    if (!entry.refreshNeeded()) {
                        // Completely unexpired cache hit. Just deliver the response.
                        mDelivery.postResponse(request, response);
                    } else {
                        // Soft-expired cache hit. We can deliver the cached response,
                        // but we need to also send the request to the network for
                        // refreshing.
                        request.addMarker("cache-hit-refresh-needed");
                        request.setCacheEntry(entry);
    
                        // Mark the response as intermediate.
                        response.intermediate = true;
    
                        // Post the intermediate response back to the user and have
                        // the delivery then forward the request along to the network.
                        final Request<?> finalRequest = request;
                        mDelivery.postResponse(request, response, new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    mNetworkQueue.put(finalRequest);
                                } catch (InterruptedException e) {
                                    // Not much we can do about this.
                                }
                            }
                        });
                    }
                } catch (Exception e) {
                    VolleyLog.e(e, "Unhandled exception %s", e.toString());
                }
            }
        }
    

    这个方法的流程

    1. 判断请求是否退出,退出就执行一个请求
    2. 尝试去获取缓存,如果没有就交给NetworkQueue处理
    3. 如果找到缓存就判断是否过期,如果过期就交给NetworkQueuue处理
    4. 再判断是否需要刷新,如不需要就直接将缓存返回
    5. 如果需要刷新就交给NetworkQueue处理

    再来看下NetworkDispatcher的run方法:

    
        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            Request<?> request;
            while (true) {
                long startTimeMs = SystemClock.elapsedRealtime();
                // release previous request object to avoid leaking request object when mQueue is drained.
                request = null;
                try {
                    // Take a request from the queue.
                    request = mQueue.take();
                } catch (InterruptedException e) {
                    // We may have been interrupted because it was time to quit.
                    if (mQuit) {
                        return;
                    }
                    continue;
                }
    
                try {
                    request.addMarker("network-queue-take");
    
                    // If the request was cancelled already, do not perform the
                    // network request.
                    if (request.isCanceled()) {
                        request.finish("network-discard-cancelled");
                        continue;
                    }
    
                    addTrafficStatsTag(request);
    
                    // Perform the network request.
                    NetworkResponse networkResponse = mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
    
                    // If the server returned 304 AND we delivered a response already,
                    // we're done -- don't deliver a second identical response.
                    if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                        continue;
                    }
    
                    // Parse the response here on the worker thread.
                    Response<?> response = request.parseNetworkResponse(networkResponse);
                    request.addMarker("network-parse-complete");
    
                    // Write to cache if applicable.
                    // TODO: Only update cache metadata instead of entire record for 304s.
                    if (request.shouldCache() && response.cacheEntry != null) {
                        mCache.put(request.getCacheKey(), response.cacheEntry);
                        request.addMarker("network-cache-written");
                    }
    
                    // Post the response back.
                    request.markDelivered();
                    mDelivery.postResponse(request, response);
                } catch (VolleyError volleyError) {
                    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                    parseAndDeliverNetworkError(request, volleyError);
                } catch (Exception e) {
                    VolleyLog.e(e, "Unhandled exception %s", e.toString());
                    VolleyError volleyError = new VolleyError(e);
                    volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                    mDelivery.postError(request, volleyError);
                }
            }
        }
    

    这个方法的流程:
    1.判断请求是否已经退出
    2.调用mNetwork.performRequest(request)方法去处理真正的网络请求,实现在BasicNetwork这个类中
    3.根据request类型的不同,解析成不同的Response
    4.然后在加入缓存并返回

    3.对比OkHttp与Volley

    1. 物理大小:OkHttp比Volley大不少
    2. 功能:OkHttp比Volley强大而稳定,强大比如OkHttp支持https,volley不支持,稳定比如OkHttp底层使用HttpURLConnection,不用担心android版本迭代,大文件下载
      Okhttp优于volley
    3. 使用场景:volley请求比较平凡,数据量比较小的情况,okHttp则适用请求数据量比较大的情况

    相关文章

      网友评论

          本文标题:Volley框架源码分析

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