Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android網絡編程(四)從源碼解析Volley

Android網絡編程(四)從源碼解析Volley

編輯:關於Android編程

1.Volley結構圖

這裡寫圖片描述

從上圖可以看到Volley分為三個線程,分別是主線程、緩存調度線程、和網絡調度線程,首先請求會加入緩存隊列,如果發現可以找到相應的緩存結果就直接讀取緩存並解析,然後回調給主線程;如果在緩存中沒有找到結果,則將這條請求加入到網絡隊列中,然後發送HTTP請求,解析響應並寫入緩存,並回調給主線程。

2.從RequestQueue入手

我們都知道使用Volley之前首先要創建RequestQueue:

 RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());

這也是volley運作的入口,看看newRequestQueue:

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

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        return newRequestQueue(context, stack, -1);
    }

連續調用了兩個重載函數,最終調用的是:

public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        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 var7) {
            ;
        }

        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;
        if(maxDiskCacheBytes <= -1) {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        } else {
            queue1 = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network1);
        }

        queue1.start();
        return queue1;
    }

可以看到如果android版本大於等於2.3則調用基於HttpURLConnection的HurlStack,否則就調用基於HttpClient的HttpClientStack。並創建了RequestQueue,調用了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()方法,在循環中調用了NetworkDispatcher的start()方法,NetworkDispatcher是網絡調度線程,默認情況下mDispatchers.length為4,默認開啟了4個網絡調度線程,也就是說有5個線程在後台運行並等待請求的到來。接下來我們創建各種的Request,並調用RequestQueue的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 var8 = this.mWaitingRequests;
            synchronized(this.mWaitingRequests) {
                String cacheKey = request.getCacheKey();

       //之前是否有執行相同的請求且還沒有返回結果的,如果有的話將此請求加入mWaitingRequests隊列,不再重復請求
                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 {
   //沒有的話就將請求加入緩存隊列mCacheQueue,同時加入mWaitingRequests中用來做下次同樣請求來時的重復判斷依據
                    this.mWaitingRequests.put(cacheKey, (Object)null);
                    this.mCacheQueue.add(request);
                }

                return request;
            }
        }
    }

通過判斷request.shouldCache(),來判斷是否可以緩存,默認是可以緩存的,如果不能緩存,則將請求添加到網絡請求隊列中,如果能緩存就判斷之前是否有執行相同的請求且還沒有返回結果的,如果有的話將此請求加入mWaitingRequests隊列,不再重復請求;沒有的話就將請求加入緩存隊列mCacheQueue,同時加入mWaitingRequests中用來做下次同樣請求來時的重復判斷依據。
從上面可以看出RequestQueue的add()方法並沒有做什麼請求網絡或者對緩存進行操作。當將請求添加到網絡請求隊列或者緩存隊列時,這時在後台的網絡調度線程和緩存調度線程輪詢各自的請求隊列發現有請求任務則開始執行,我們先看看緩存調度線程。

3.CacheDispatcher緩存調度線程

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 {
                        //獲取緩存隊列中的一個請求
                            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");
                                    //對數據進行解析並回調給主線程
                                    Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
                                    e.addMarker("cache-hit-parsed");
                                    if(!entry.refreshNeeded()) {
                                        this.mDelivery.postResponse(e, response);
                                    } else {
                                        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 {
                                    e.addMarker("cache-hit-expired");
                                    e.setCacheEntry(entry);
                                    this.mNetworkQueue.put(e);
                                }
                            }
                        } catch (InterruptedException var4) {
                            if(this.mQuit) {
                                return;
                            }
                        }
                    }
                }
            }
        }
    }

    static {
        DEBUG = VolleyLog.DEBUG;
    }

看到四個while循環有些暈吧,讓我們挑重點的說,首先從緩存隊列取出請求,判斷是否請求是否被取消了,如果沒有則判斷該請求是否有緩存的響應,如果有並且沒有過期則對緩存響應進行解析並回調給主線程。接下來看看網絡調度線程。

4.NetworkDispatcher網絡調度線程

NetworkDispatcher的run()方法:

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

        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 {
                    this.addTrafficStatsTag(request);
                    //請求網絡
                    NetworkResponse e = this.mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
                    if(e.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        Response volleyError1 = request.parseNetworkResponse(e);
                        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);
                    }
                }
            } 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),這個mNetwork是一個接口,實現它的類是BasicNetwork,我們來看看BasicNetwork的performRequest()方法:

  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());
                httpResponse = this.mHttpStack.performRequest(request, e);
                StatusLine statusCode1 = httpResponse.getStatusLine();
                int networkResponse1 = statusCode1.getStatusCode();
                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                if(networkResponse1 == 304) {
                    Entry requestLifetime2 = request.getCacheEntry();
                    if(requestLifetime2 == null) {
                        return new NetworkResponse(304, (byte[])null, responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
                    }

                    requestLifetime2.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(304, requestLifetime2.data, requestLifetime2.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart);
                }


...省略

從上面可以看到在12行調用的是HttpStack的performRequest()方法請求網絡,接下來根據不同的響應狀態碼來返回不同的NetworkResponse。另外HttpStack也是一個接口,實現它的兩個類我們在前面已經提到了就是HurlStack和HttpClientStack。讓我們再回到NetworkDispatcher,請求網絡後,會將響應結果存在緩存中,如果響應結果成功則調用this.mDelivery.postResponse(request, volleyError1)來回調給主線程。來看看Delivery的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));
    }

來看看ResponseDeliveryRunnable裡面做了什麼:

 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行調用了this.mRequest.deliverResponse(this.mResponse.result),這個就是實現Request抽象類必須要實現的方法,我們來看看StringRequest的源碼:

public class StringRequest extends Request {
    private final Listener mListener;

    public StringRequest(int method, String url, Listener listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mListener = listener;
    }

    public StringRequest(String url, Listener listener, ErrorListener errorListener) {
        this(0, url, listener, errorListener);
    }

    protected void deliverResponse(String response) {
        this.mListener.onResponse(response);
    }

 ...省略
}

在deliverResponse方法中調用了this.mListener.onResponse(response),最終將response回調給了Response.Listener的onResponse()方法。我們用StringRequest請求網絡的寫法是這樣的:

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());
        StringRequest mStringRequest = new StringRequest(Request.Method.GET, "http://www.baidu.com",
                new Response.Listener() {
                    @Override
                    public void onResponse(String response) {
                        Log.i("wangshu", response);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e("wangshu", error.getMessage(), error);
            }
        });
        //將請求添加在請求隊列中
        mQueue.add(mStringRequest);

看到第5行整個Volley的大致流程都通了吧,好了關於Volley的源碼就講到這裡。

   
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved