Android網絡通訊Volley框架源代碼淺析(一)

尊重原創http://blog.csdn.net/yuanzeyao/article/details/25837897html


       從今天開始,我打算爲你們呈現關於Volley框架的源代碼分析的文章,Volley框架是Google在2013年公佈的。主要用於實現頻繁而且粒度比較細小的Http請求。在此以前Android中進行Http請求通常是使用HttpUrlConnection和HttpClient進行,但是使用起來很是麻煩,而且效率比較地下,我想谷歌正式基於此種緣由公佈了Volley框架。事實上出了Volley框架意外,也有一些http請求開源項目。比方使用比較普遍的有async-http,UniversImageLoader等等,當中async-http主要用來實現異步http請求,然後者主要用來請求圖片。Volley具備以上兩種框架的功能,而且是Google公司公佈。我想做爲Android開發人員,很是有必要研究一下該框架。java


一、下載Volley框架
git clone https://android.googlesource.com/platform/frameworks/volley

二、引用Volley框架
引用該框架的方式主要有兩種:
(1):直接導入Volley框架,做爲lib使用
(2):編譯Volley成jar包

三、Volley的功能:
前面已經說起了Volley是一個用於http請求的框架,其主要功能例如如下:
json,xml,String,Image等資源的請求,固然咱們還可以依據本身的需要來改寫Volley框架源代碼。從而實現本身的功能
android


四、Volley源代碼分析
git

舒適提醒:假設是第一次看Volley源代碼,第一遍沒看懂不要緊,將源代碼copy下來,跟着個人思路慢慢分析,將文章從頭到後多看幾遍就ok了。因爲Volley的一些關鍵類都互對應用。我僅僅能一個一個的分析了,等你看完我所有的文章,而後再從頭看一變。相信你定有所收穫json


固然閱讀次文章以前。最好學會知道Volley框架的基本使用,因爲網絡上很是多類似的教程。我在此處就再也不描寫敘述了,後期假設有時間我也會解說一下Volley的應用
咱們就從Volley這個類開始吧
數組

(1) Volley.java
緩存

public class Volley {

    //緩存文件夾
    private static final String DEFAULT_CACHE_DIR = "volley";
    //建立一個默認的請求隊列。咱們的請求建立好後。放入該隊列就能夠
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
		//緩存文件夾
        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) {
			
			/**
				假設咱們沒有傳入stack,那麼本身建立一個,假設sdk>9(就是2.3以上)。那麼使用
				HttpURLConnection實現http請求,假設2.3曾經使用HttpClient實現,因爲在2.3曾經httpURLConnection不穩定
			*/
            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類是一個網絡請求類,使用stack進行網絡請求
        Network network = new BasicNetwork(stack);
		//真正建立一個請求隊列,傳入一個磁盤緩存和網絡請求類
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
		//啓動請求隊列。事實上裏面就是啓動了一些線程。不斷監聽是否有請求
        queue.start();

        return queue;
    }

    /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
}


Volley類主要用來建立一個請求隊列,咱們的不論什麼請求(請求字符串,json,xml)都放入到這個隊列中(事實上裏面有兩個隊列,後面咱們慢慢學習,臨時簡單理解爲一個)。

建立完隊列後。調用start方法,就會啓動一些線程(臨時不關注多少條線程),不斷監聽隊裏裏面是否有請求,假設有請求則運行http請求,在2.3以前的版本號中。Http請求是經過httpClient實現。在2.3之後的版本號中是經過HttpURLConnection實現,因爲在2.3以前的版本號中HttpRlConnection很是不穩定網絡


(2) HttpStack.java
如下看看HttpStack是何方神聖
app

public interface HttpStack {
    /**
     名字挺嚇人的,呵呵,事實上就是一個接口,它有兩個實現。各自是HurlStack,HttpClientStack,經過名字你們
	 可以猜出來一個基於HttpClient,一個基於HttpURLConnection
     * @return the HTTP response
     */
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
        throws IOException, AuthFailureError;

}

直接查看它的子類方法吧
首先看  HurlStack.java類
這個類是基於HttpURLConnection實現的
因爲這個類比較長,我就重點解說一下


(3) HurlStack.java框架

/**
	繼承自HttpStack,咱們臨時就把Request抽象成一個請求。包括url,method等信息,後期咱們會重點分析這個類
	第二個參數就是一些請求頭信息
	*/
    @Override
    public HttpResponse performRequest(Request<?

> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { String url = request.getUrl(); HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); map.putAll(additionalHeaders); //此處通常爲空,咱們直接忽略掉 if (mUrlRewriter != null) { String rewritten = mUrlRewriter.rewriteUrl(url); if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } URL parsedUrl = new URL(url); HttpURLConnection connection = openConnection(parsedUrl, request); //增長請求頭 for (String headerName : map.keySet()) { connection.addRequestProperty(headerName, map.get(headerName)); } //這種方法名字很是長。事實上功能很是easy,就是爲connection設置請求方法 如get post等等 setConnectionParametersForRequest(connection, request); // Initialize HttpResponse with data from the HttpURLConnection. ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1); int responseCode = connection.getResponseCode(); if (responseCode == -1) { // -1 is returned by getResponseCode() if the response code could not be retrieved. // Signal to the caller that something was wrong with the connection. throw new IOException("Could not retrieve response code from HttpUrlConnection."); } StatusLine responseStatus = new BasicStatusLine(protocolVersion, connection.getResponseCode(), connection.getResponseMessage()); BasicHttpResponse response = new BasicHttpResponse(responseStatus); response.setEntity(entityFromConnection(connection)); for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) { if (header.getKey() != null) { Header h = new BasicHeader(header.getKey(), header.getValue().get(0)); response.addHeader(h); } } //http的返回結果 return response; }


(4) HttpClientStack.java

/**
	相對照上一個方法簡單。相信使用過httpClient的同窗一看就明確
	*/
    @Override
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
			//這種方法見名知意。就是建立一個HttpGet或者HttpPost
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
		//增長頭信息
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());
		//在請求以前進行準備工做,事實上是個空方法。很是想AsyncTask的onPreExecute
        onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
		//運行請求並返回結果
        return mClient.execute(httpRequest);
    }

看到這裏你們確定認爲這個框架也沒有什麼了不得嘛,和使用HttpURLConnection和HttpClient差點兒相同嘛。假設你真的這樣認爲那麼你就大錯特錯了,事實上這個框架的核心在於線程的調度和緩存上面,後期咱們會介紹的


回到Volley類。咱們看下一個陌生的類就是Network,事實上Network只是是個接口而已,它的實現類是BaskNetwork


(5) BaskicNetwork.java

從名字咱們就可以看出來。這個類就是進行網絡請求的。事實上他就是對HttpurlStack或者HttpClientStack的一個封裝,真正實現請求的仍是上面兩個類。
最核心的方法:

@Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = new HashMap<String, String>();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
				//調用mHttpStack運行http請求
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
					//將返回結果封裝成一個NetworkResponse
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                            request.getCacheEntry() == null ?

null : request.getCacheEntry().data, responseHeaders, true); } // Some responses such as 204s do not have content. We must check. if (httpResponse.getEntity() != null) { responseContents = entityToBytes(httpResponse.getEntity()); } else { // Add 0 byte response as a way of honestly representing a // no-content request. responseContents = new byte[0]; } // if the request is slow, log it. long requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299) { throw new IOException(); } return new NetworkResponse(statusCode, responseContents, responseHeaders, false); } catch (SocketTimeoutException e) { //超時又一次請求 attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException e) { //超時又一次請求 attemptRetryOnException("connection", request, new TimeoutError()); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); if (responseContents != null) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false); if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { throw new NetworkError(networkResponse); } } } }


這種方法調用了mHttpStack的同名方法,僅僅只是在mHttpStack中返回的是HttpResponse。在這裏返回的是NetworkResponse。



而後再看看本篇文章的最後一個類:
RequestQueue.java
我保留了一些keyword段,刪除不影響理解的字段


public class RequestQueue {

	...

    //本地緩存隊列。假設一個請求可以緩存,那麼先放到這個隊列中,假設本地緩存沒有命中,則增長網絡隊列。見後面
    private final PriorityBlockingQueue<Request<?

>> mCacheQueue = new PriorityBlockingQueue<Request<?>>(); //網絡請求隊列 private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?

>>(); //默認的網絡請求線程個數 默認四個,這個咱們可以修改 private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; //本地緩存的接口 private final Cache mCache; //這個類相信你們並不陌生 private final Network mNetwork; //因爲網絡請求在子線程中運行,這個對象將請求結果發送到ui線程,功能很是像Handler private final ResponseDelivery mDelivery; //網絡線程數組 private NetworkDispatcher[] mDispatchers; //本地線程數組 僅僅有一條 private CacheDispatcher mCacheDispatcher; /** 建立一個請求隊列 參數1:本地緩存 參數2: network 進行網絡進行的包裝類 參數3:網絡請求線程池大小 參數4:就是一個將子線程的數據發送到ui線程的功能類,先可以不用關心 */ public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } /** * Creates the worker pool. Processing will not begin until {@link #start()} is called. * * @param cache A Cache to use for persisting responses to disk * @param network A Network interface for performing HTTP requests */ public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); } //啓動本地和網絡線程 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(); } } //關閉本地和網絡線程 public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } } //至關於一個過濾器,對於apply方法返回true的Request可以從Queue中刪除 public interface RequestFilter { public boolean apply(Request<?> request); } //藉助上面的方法實現對沒有運行的Request進行刪除 public void cancelAll(RequestFilter filter) { synchronized (mCurrentRequests) { for (Request<?> request : mCurrentRequests) { if (filter.apply(request)) { request.cancel(); } } } } //取消所有的請求 public void cancelAll(final Object tag) { if (tag == null) { throw new IllegalArgumentException("Cannot cancelAll with a null tag"); } cancelAll(new RequestFilter() { @Override public boolean apply(Request<?

> request) { return request.getTag() == tag; } }); } /** 將Request放入兩個隊列中的一個 */ 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"); // 假設該Request不能緩存,那麼直接放入網絡隊列 if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // 把Request放入具備一樣CacheKey的鏈表中,假設沒有一樣的CacheKey的Request請求存在,則放入本地隊列 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; } } /** * 結束一個Request */ void finish(Request<?> request) { // Remove from the set of requests currently being processed. synchronized (mCurrentRequests) { mCurrentRequests.remove(request); } if (request.shouldCache()) { synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey); if (waitingRequests != null) { if (VolleyLog.DEBUG) { VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.", waitingRequests.size(), cacheKey); } // Process all queued up requests. They won't be considered as in flight, but // that's not a problem as the cache has been primed by 'request'. mCacheQueue.addAll(waitingRequests); } } } } }


寫到這裏先高一段落吧,來個小小的總結:Volley中有一個RequestQueue(包括本地隊列和網絡隊列),就是請求隊列。每一個http請求都被封裝成了一個Request,經過隊列的add方法增長隊列,假設一個Request可以緩存,那麼先增長本地隊列。假設不能緩存則增長網絡隊列



歡迎繼續閱讀Volley框架淺析(二)

歡迎留言討論。。。

相關文章
相關標籤/搜索