網(wǎng)絡(luò)庫(kù)簡(jiǎn)單總結(jié)

網(wǎng)絡(luò)庫(kù)的介紹

1.HttpURLConnection

API簡(jiǎn)單,體積較小,因而非常適用于Android項(xiàng)目,但是在android 2.2及以下版本中HttpUrlConnection存在著一些bug,所以建議在android 2.3以后使用HttpUrlConnection桦锄,在這之前使用的是HttpClient。

2.HttpClient (Apache )

高效穩(wěn)定蔫耽,但是維護(hù)成本高昂结耀,故android 開發(fā)團(tuán)隊(duì)不愿意維護(hù)該庫(kù)更青睞輕便的HttpUrlConnection。Android 5.0后已廢棄該庫(kù)匙铡。

3.OKHttp

Square公司產(chǎn)品图甜,OkHttp相比HttpURLConnection和HttpClient功能更加強(qiáng)大。

4.Volley

Volley是在2013年Google I/O大會(huì)上推出了一個(gè)新的網(wǎng)絡(luò)通信框架鳖眼,內(nèi)部封裝了HttpURLConnection和HttpClient, 解決了網(wǎng)絡(luò)數(shù)據(jù)解析和線程切換的問題黑毅。
主要用于解決通訊頻率高,但傳輸數(shù)據(jù)量小的情景而對(duì)于大數(shù)據(jù)量的網(wǎng)絡(luò)操作,比如說(shuō)下載文件等钦讳,Volley的表現(xiàn)就會(huì)非常糟糕矿瘦。

其實(shí)Volley的使用是很簡(jiǎn)單的,總的來(lái)說(shuō)就是發(fā)送一個(gè)http的請(qǐng)求,將請(qǐng)求加入到RequestQueue(請(qǐng)求隊(duì)列)中,這里的RequestQueue是一個(gè)請(qǐng)求隊(duì)列對(duì)象,它可以緩存所有的HTTP請(qǐng)求愿卒,然后按照一定的算法并發(fā)地發(fā)出這些請(qǐng)求缚去。RequestQueue內(nèi)部的設(shè)計(jì)就是非常合適高并發(fā)的,因此我們不必為每一次HTTP請(qǐng)求都創(chuàng)建一個(gè)RequestQueue對(duì)象琼开,這是非常浪費(fèi)資源的易结,基本上在每一個(gè)需要和網(wǎng)絡(luò)交互的Activity中創(chuàng)建一個(gè)RequestQueue對(duì)象就足夠了。

總的來(lái)說(shuō)我們常用的Volley就下面三個(gè)步驟:Volley.newRequestQueue(context).add(request);
1. 創(chuàng)建一個(gè)RequestQueue對(duì)象柜候。
2. 創(chuàng)建一個(gè)StringRequest對(duì)象衬衬。
3. 將StringRequest對(duì)象添加到RequestQueue里面。
下面我們主要通過這三句話來(lái)分析一下Volley源碼中的實(shí)現(xiàn)原理

volley.png

這是官方的關(guān)于Volley工作流程圖 , 其中藍(lán)色部分代表主線程改橘,綠色部分代表緩存線程,橙色部分代表網(wǎng)絡(luò)線程玉控。我們?cè)谥骶€程中調(diào)用RequestQueue的add()方法來(lái)添加一條網(wǎng)絡(luò)請(qǐng)求飞主,這條請(qǐng)求會(huì)先被加入到緩存隊(duì)列當(dāng)中,如果發(fā)現(xiàn)可以找到相應(yīng)的緩存結(jié)果就直接讀取緩存并解析高诺,然后回調(diào)給主線程碌识。如果在緩存中沒有找到結(jié)果,則將這條請(qǐng)求加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中虱而,然后處理發(fā)送HTTP請(qǐng)求筏餐,解析響應(yīng)結(jié)果,寫入緩存牡拇,并回調(diào)主線程魁瞪。

第一步:newRequestQueue(context)

 public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);  //執(zhí)行帶兩個(gè)參數(shù)的構(gòu)造方法
    }

這個(gè)方法僅僅只有一行代碼穆律,只是調(diào)用了newRequestQueue()的方法重載,并給第二個(gè)參數(shù)傳入null导俘。那我們看下帶有兩個(gè)參數(shù)的newRequestQueue()方法中的代碼峦耘,如下所示:

 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是等于null的,則去創(chuàng)建一個(gè)HttpStack對(duì)象
            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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

在上面的代碼中可以看出,如果stack是等于null的旅薄,則去創(chuàng)建一個(gè)HttpStack對(duì)象辅髓,這里會(huì)判斷如果手機(jī)系統(tǒng)版本號(hào)是大于9(這里指SDK版本)的,則創(chuàng)建一個(gè)HurlStack的實(shí)例少梁,否則就創(chuàng)建一個(gè)HttpClientStack的實(shí)例洛口。實(shí)際上HurlStack的內(nèi)部就是使用HttpURLConnection進(jìn)行網(wǎng)絡(luò)通訊的,而HttpClientStack的內(nèi)部則是使用HttpClient進(jìn)行網(wǎng)絡(luò)通訊的,至于為什么說(shuō)需要大于或等于9,是因?yàn)镾DK為9時(shí)對(duì)應(yīng)的系統(tǒng)為android2.3,版本2.3以后推薦使用HttpURLConnection.
創(chuàng)建好了HttpStack之后凯沪,接下來(lái)又創(chuàng)建了一個(gè)Network對(duì)象第焰,它是用于根據(jù)傳入的HttpStack對(duì)象來(lái)處理網(wǎng)絡(luò)請(qǐng)求的,緊接著new出一個(gè)RequestQueue對(duì)象著洼,并調(diào)用它的start()方法進(jìn)行啟動(dòng)樟遣,然后將RequestQueue返回,這樣newRequestQueue()的方法就執(zhí)行結(jié)束了身笤。接下啦我們看看RequestQueue的start()方法內(nèi)部執(zhí)行內(nèi)容:

    /** Number of network request dispatcher threads to start. */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;//默認(rèn)網(wǎng)絡(luò)請(qǐng)求線程數(shù)量

    /**
     * Starts the dispatchers in this queue.
     */
    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();
        }
    }

這里先是創(chuàng)建了一個(gè)CacheDispatcher的實(shí)例豹悬,然后調(diào)用了它的start()方法,接著在一個(gè)for循環(huán)里去創(chuàng)建NetworkDispatcher的實(shí)例液荸,并分別調(diào)用它們的start()方法瞻佛。這里的CacheDispatcher和NetworkDispatcher都是繼承自Thread的,而默認(rèn)情況下for循環(huán)會(huì)執(zhí)行四次娇钱,也就是說(shuō)當(dāng)調(diào)用了Volley.newRequestQueue(context)之后伤柄,就會(huì)有五個(gè)線程一直在后臺(tái)運(yùn)行,不斷等待網(wǎng)絡(luò)請(qǐng)求的到來(lái)文搂,其中CacheDispatcher是緩存線程适刀,NetworkDispatcher是網(wǎng)絡(luò)請(qǐng)求線程。

上面得到了RequestQueue之后煤蹭,我們只需要構(gòu)建出相應(yīng)的Request笔喉,然后調(diào)用RequestQueue的add()方法將Request傳入就可以完成網(wǎng)絡(luò)請(qǐng)求操作了,下面我們分析一下add()方法的內(nèi)部的邏輯

 /**
     * Adds a Request to the dispatch queue.
     * @param request The request to service
     * @return The passed-in 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");

        // If the request is uncacheable, skip the cache queue and go straight to the network.
        if (!request.shouldCache()) {  //判斷當(dāng)前的請(qǐng)求是否可以緩存
            mNetworkQueue.add(request);  //如果不能緩存則直接將這條請(qǐng)求加入網(wǎng)絡(luò)請(qǐng)求隊(duì)列
            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);  //可以緩存的話則在將這條請(qǐng)求加入緩存隊(duì)列
            }
            return request;
        }
    }

可以看到硝皂,開始的時(shí)候會(huì)判斷當(dāng)前的請(qǐng)求是否可以緩存常挚,如果不能緩存則直接將這條請(qǐng)求加入網(wǎng)絡(luò)請(qǐng)求隊(duì)列,可以緩存的話則將這條請(qǐng)求加入緩存隊(duì)列稽物。在默認(rèn)情況下奄毡,每條請(qǐng)求都是可以緩存的,當(dāng)然我們也可以調(diào)用Request的setShouldCache(false)方法來(lái)改變這一默認(rèn)行為贝或。
既然默認(rèn)每條請(qǐng)求都是可以緩存的吼过,自然就被添加到了緩存隊(duì)列中锐秦,于是一直在后臺(tái)等待的緩存線程就要開始運(yùn)行起來(lái)了,我們看下CacheDispatcher中的run()方法那先,代碼如下所示:

public class CacheDispatcher extends Thread {
    ......  //省略代碼
    @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();

        while (true) {   //死循環(huán)
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request<?> request = mCacheQueue.take();
                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());  //從緩存當(dāng)中取出響應(yīng)結(jié)果
                if (entry == null) {  //如何為空的話則把這條請(qǐng)求加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中
                    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()) {  //如果不為空的但該緩存已過期农猬,則同樣把這條請(qǐng)求加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中
                    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));   //對(duì)數(shù)據(jù)進(jìn)行解析
                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.
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}

可以看到一個(gè)while(true)循環(huán),說(shuō)明緩存線程始終是在運(yùn)行的售淡,接著會(huì)嘗試從緩存當(dāng)中取出響應(yīng)結(jié)果斤葱,如何為空的話則把這條請(qǐng)求加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中,如果不為空的話再判斷該緩存是否已過期揖闸,如果已經(jīng)過期了則同樣把這條請(qǐng)求加入到網(wǎng)絡(luò)請(qǐng)求隊(duì)列中揍堕,否則就認(rèn)為不需要重發(fā)網(wǎng)絡(luò)請(qǐng)求,直接使用緩存中的數(shù)據(jù)即可汤纸。之后會(huì)調(diào)用Request的parseNetworkResponse()方法來(lái)對(duì)數(shù)據(jù)進(jìn)行解析衩茸,再往后就是將解析出來(lái)的數(shù)據(jù)進(jìn)行回調(diào)了,這部分代碼的邏輯和NetworkDispatcher后半部分的邏輯是基本相同的贮泞,那么我們等下合并在一起看就好了楞慈,先來(lái)看一下NetworkDispatcher中是怎么處理網(wǎng)絡(luò)請(qǐng)求隊(duì)列的,代碼如下所示:

public class NetworkDispatcher extends Thread {
    .......//省略代碼
    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {  //死循環(huán)
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            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);  //執(zhí)行網(wǎng)絡(luò)請(qǐng)求
                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);  //解析響應(yīng)的網(wǎng)絡(luò)數(shù)據(jù)
                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);
            }
        }
    }

    private void parseAndDeliverNetworkError(Request<?> request, VolleyError error) {
        error = request.parseNetworkError(error);
        mDelivery.postError(request, error);
    }
}

同樣地啃擦,我們看到了類似的while(true)循環(huán)囊蓝,說(shuō)明網(wǎng)絡(luò)請(qǐng)求線程也是在不斷運(yùn)行的。在死循環(huán)中會(huì)調(diào)用Network的performRequest()方法來(lái)去發(fā)送網(wǎng)絡(luò)請(qǐng)求令蛉,而Network是一個(gè)接口聚霜,我們?cè)谏厦鎰?chuàng)建 stack是通過這段代碼(Network network = new BasicNetwork(stack);)實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求的,所以這里具體的實(shí)現(xiàn)是BasicNetwork,所以我們?cè)贐asicNetwork類中查看performRequest()方法珠叔,如下所示:

public class BasicNetwork implements Network {
   .....//省略代碼
    @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
                httpResponse = mHttpStack.performRequest(request, headers); //調(diào)用了HttpStack的performRequest()方法
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);  //組裝成一個(gè)NetworkResponse對(duì)象進(jìn)行返回
                    }
                    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
                    entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart); //組裝成一個(gè)NetworkResponse對(duì)象進(jìn)行返回
                }

                // 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,
                        SystemClock.elapsedRealtime() - requestStart);
            } catch (SocketTimeoutException e) {
                    ...... //省略代碼
            }
        }
    }
}

這段代碼主要是一些網(wǎng)絡(luò)請(qǐng)求細(xì)節(jié)方面的東西蝎宇,需要注意的是調(diào)用了HttpStack的performRequest()方法,這里的HttpStack就是在一開始調(diào)用newRequestQueue()方法是創(chuàng)建的實(shí)例祷安,默認(rèn)情況下如果系統(tǒng)版本號(hào)大于9就創(chuàng)建的HurlStack對(duì)象姥芥,否則創(chuàng)建HttpClientStack對(duì)象。這兩個(gè)對(duì)象的內(nèi)部實(shí)際就是分別使用HttpURLConnection和HttpClient來(lái)發(fā)送網(wǎng)絡(luò)請(qǐng)求的汇鞭,之后會(huì)將服務(wù)器返回的數(shù)據(jù)組裝成一個(gè)NetworkResponse對(duì)象進(jìn)行返回撇眯。

在NetworkDispatcher中收到了NetworkResponse這個(gè)返回值后又會(huì)調(diào)用Request的parseNetworkResponse()方法來(lái)解析NetworkResponse中的數(shù)據(jù),以及將數(shù)據(jù)寫入到緩存虱咧,這個(gè)方法的實(shí)現(xiàn)是交給Request的子類來(lái)完成的,因?yàn)椴煌N類的Request解析的方式也肯定不同锚国。如果想自定義Request的方式,其中parseNetworkResponse()這個(gè)方法就是必須要重寫的腕巡。
在解析完了NetworkResponse中的數(shù)據(jù)之后,又會(huì)調(diào)用ExecutorDelivery的postResponse()方法來(lái)回調(diào)解析出的數(shù)據(jù)血筑,代碼如下所示:

1.在NetworkDispatcher類中
       private final ResponseDelivery mDelivery;
 // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);   //解析數(shù)據(jù)
                request.addMarker("network-parse-complete");

                mDelivery.postResponse(request, response);  //回調(diào)解析出的數(shù)據(jù),具體實(shí)現(xiàn)在下面代碼中

2.在ExecutorDelivery類中回調(diào)解析出來(lái)的數(shù)據(jù)
public class ExecutorDelivery implements ResponseDelivery {
    @Override
    public void postResponse(Request<?> request, Response<?> response) {
        postResponse(request, response, null);
    }

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

其中绘沉,在mResponsePoster的execute()方法中傳入了一個(gè)ResponseDeliveryRunnable對(duì)象煎楣,這樣就可以保證該對(duì)象中的run()方法就是在主線程當(dāng)中運(yùn)行的了,我們看下run()方法中的代碼是什么樣的:

private class ResponseDeliveryRunnable implements Runnable {
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }

分析重點(diǎn)代碼即可,主要看Request的deliverResponse()方法车伞,這個(gè)方法也是我們?cè)谧远xRequest時(shí)需要重寫的另外一個(gè)方法择懂,每一條網(wǎng)絡(luò)請(qǐng)求的響應(yīng)都是回調(diào)到這個(gè)方法中,最后我們?cè)僭谶@個(gè)方法中將響應(yīng)的數(shù)據(jù)回調(diào)到Response.Listener的onResponse()方法中就可以了另玖。

5.Retrofit.

Square公司產(chǎn)品困曙,內(nèi)部封裝了OKhttp, 解決了網(wǎng)絡(luò)數(shù)據(jù)解析和線程切換的問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谦去,一起剝皮案震驚了整個(gè)濱河市慷丽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鳄哭,老刑警劉巖要糊,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異妆丘,居然都是意外死亡锄俄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門勺拣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奶赠,“玉大人,你說(shuō)我怎么就攤上這事宣脉〕的” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵塑猖,是天一觀的道長(zhǎng)竹祷。 經(jīng)常有香客問我,道長(zhǎng)羊苟,這世上最難降的妖魔是什么塑陵? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蜡励,結(jié)果婚禮上令花,老公的妹妹穿的比我還像新娘。我一直安慰自己凉倚,他們只是感情好兼都,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著稽寒,像睡著了一般扮碧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天慎王,我揣著相機(jī)與錄音蚓土,去河邊找鬼。 笑死赖淤,一個(gè)胖子當(dāng)著我的面吹牛蜀漆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咱旱,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼确丢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了莽龟?” 一聲冷哼從身側(cè)響起蠕嫁,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎毯盈,沒想到半個(gè)月后剃毒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搂赋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年赘阀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片脑奠。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡基公,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宋欺,到底是詐尸還是另有隱情轰豆,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布齿诞,位于F島的核電站酸休,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏祷杈。R本人自食惡果不足惜斑司,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望但汞。 院中可真熱鬧宿刮,春花似錦、人聲如沸私蕾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)踩叭。三九已至谤饭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背揉抵。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗤疯,地道東北人冤今。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像茂缚,于是被迫代替她去往敵國(guó)和親戏罢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容