成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

讀源碼學(xué)設(shè)計(jì) - volley

txgcwm / 1052人閱讀

摘要:在大行其道的今天,已經(jīng)略顯過時(shí)。我們按照上面的思路,從和作為出發(fā)點(diǎn),由淺入深來閱讀源碼。在最后看類源碼時(shí)我們可以看到這個(gè)過程。負(fù)責(zé)分發(fā),對(duì)來說,就是通過將發(fā)送到了主線程在中調(diào)用了。的個(gè)數(shù),決定了網(wǎng)絡(luò)請(qǐng)求的最大并發(fā)數(shù)。

Volley

在 retrofit+okhttp 大行其道的今天,volley 已經(jīng)略顯過時(shí)。使用 volley,我們無(wú)法避免寫一些樣板代碼,但在它剛出現(xiàn)時(shí),曾經(jīng)很大程度降低了 android 開發(fā)中網(wǎng)絡(luò)請(qǐng)求這部分的難度,簡(jiǎn)潔、輕量、容易定制擴(kuò)展,我們依然能從它的源碼中感受到這些。

看一個(gè)使用 volley 的小 demo

    

StringRequest request = new StringRequest("http://example.com", new Response
            .Listener() {
        @Override
        public void onResponse(String s) {

        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError volleyError) {

        }
    });
    
    RequestQueue requestQueue = Volley.newRequestQueue(context);
             
    requestQueue.add(request);

三步走

創(chuàng)建一個(gè) Request

創(chuàng)建一個(gè) RequestQueue

聯(lián)結(jié)兩者

這就是 volley 作為一個(gè)網(wǎng)絡(luò)請(qǐng)求庫(kù)提供給我們的用戶界面,超級(jí)簡(jiǎn)潔有沒有。Request 是一個(gè)數(shù)據(jù),是一個(gè)相對(duì)靜態(tài)的概念,我們使用它來說我們的網(wǎng)絡(luò)請(qǐng)求要發(fā)送到哪里、帶有什么樣的參數(shù)、返回的數(shù)據(jù)是什么對(duì)象、請(qǐng)求成功了怎樣處理、請(qǐng)求失敗了又要怎么辦,等等等等。RequestQueue 呢,是我們的執(zhí)行引擎,吃掉一個(gè)個(gè) Request,消費(fèi) Request 的屬性,同時(shí)返回 Response。

我們按照上面的思路,從 Request 和 RequestQueue 作為出發(fā)點(diǎn),由淺入深來閱讀 volley 源碼。

Request

前面說到,Request 在 volley 中是相對(duì)靜態(tài)的概念,靜態(tài)是好的,我們喜歡靜態(tài)的東西,所以我們從 Request 開始???Request 的定義

public abstract class Request implements Comparable> {
}

我們看到,Request 是個(gè)抽象類,有泛型,實(shí)現(xiàn)了 Comparable 接口??吹竭@里你也許可以大膽推測(cè),這個(gè) Request 是可以大大擴(kuò)展的,大多數(shù)網(wǎng)絡(luò)請(qǐng)求以表單的方式作為參數(shù),十分多變,那么這個(gè)泛型來指名返回結(jié)果的類型應(yīng)該是十分合理的。沒錯(cuò),就是這樣。實(shí)現(xiàn) Comparable 呢,Request 的比較有什么含義?想到上面有 Queue 的概念,這個(gè) Comaparable 也許和請(qǐng)求的優(yōu)先級(jí)有關(guān),對(duì)的,事實(shí)上 RequestQueue 中的確使用了 PriorityBlockingQueue 來達(dá)到管理 Request 優(yōu)先級(jí)的效果,后文再細(xì)說。

上面簡(jiǎn)單的一行代碼已經(jīng)向我們透漏了大量信息,讓我們更近一步??纯?Request 有哪些屬性

public abstract class Request implements Comparable> {
    /**
     * Request method of this request.  Currently supports GET, POST, PUT, DELETE, HEAD, OPTIONS,
     * TRACE, and PATCH.
     */
    private final int mMethod;

    /** URL of this request. */
    private final String mUrl;

    /** Default tag for {@link TrafficStats}. */
    private final int mDefaultTrafficStatsTag;

    /** Listener interface for errors. */
    private final Response.ErrorListener mErrorListener;

    /** Sequence number of this request, used to enforce FIFO ordering. */
    private Integer mSequence;

    /** The request queue this request is associated with. */
    private RequestQueue mRequestQueue;

    /** Whether or not responses to this request should be cached. */
    private boolean mShouldCache = true;

    /** Whether or not this request has been canceled. */
    private boolean mCanceled = false;

    /** Whether or not a response has been delivered for this request yet. */
    private boolean mResponseDelivered = false;

    /** Whether the request should be retried in the event of an HTTP 5xx (server) error. */
    private boolean mShouldRetryServerErrors = false;

    /** The retry policy for this request. */
    private RetryPolicy mRetryPolicy;

    /**
     * When a request can be retrieved from cache but must be refreshed from
     * the network, the cache entry will be stored here so that in the event of
     * a "Not Modified" response, we can be sure it hasn"t been evicted from cache.
     */
    private Cache.Entry mCacheEntry = null;

    /** An opaque token tagging this request; used for bulk cancellation. */
    private Object mTag;
}

我們簡(jiǎn)單的分下類:

mMethod、mUrl:用什么 Http 方法給誰(shuí)發(fā)請(qǐng)求,mUrl 中可以附帶參數(shù)(如果想在 body 中傳遞參數(shù)呢,想定制 Header 呢)

mShouldCache、mCanceled、mCacheEntry:緩存相關(guān)

mRequestQueue:關(guān)聯(lián)的 RequestQueue,結(jié)束 Request 時(shí)使用

mErrorListener:錯(cuò)誤回調(diào),想想上面的 demo,還有一個(gè)成功的回調(diào),但這里并沒有,下文再說

mShouldRetryServerErrors、mRetryPolicy:控制重試

mTag:不同的 Request 可以設(shè)置相關(guān)的 tag,這樣我們可以用 tag 來批量取消 Request

mSequence、mDefaultTrafficStatsTag:忽略

想一想當(dāng)我們自定義一個(gè) Request 的時(shí)候會(huì)關(guān)心那些屬性呢,Method、Url、Cache、ErrorListener,大概就這樣吧。

在上面的分類中還有一些問題要解決,一個(gè)一個(gè)來。

Body
    public byte[] getBody() throws AuthFailureError {
        Map params = getParams();
        if (params != null && params.size() > 0) {
            return encodeParameters(params, getParamsEncoding());
        }
        return null;
    }

這是個(gè) public 方法,我們可以 override 它。不急,可以看到它使用了 getParams()、encodeParameters()、getParamsEncoding() 方法,我們 override 這三個(gè)方法也可以達(dá)到間接影響 Body 的效果,encodeParameters() 是 private 的,還好一般沒有改變它的必要。

Header
public Map getHeaders() throws AuthFailureError {
    return Collections.emptyMap();
}

override!

請(qǐng)求成功的回調(diào)
abstract protected void deliverResponse(T response);
另外一個(gè)比較重要的方法
   

     abstract protected Response parseNetworkResponse(NetworkResponse response);

將 NetworkResponse 轉(zhuǎn)化為客戶端的類型,這是一個(gè) abstract 方法,我們自定義的 Request 子類必須實(shí)現(xiàn)。事實(shí)上,demo 中的 StringRequest 就是 volley 中的一個(gè) Request 的具體實(shí)現(xiàn),我們可以從中學(xué)習(xí):

@Override
protected Response parseNetworkResponse(NetworkResponse response) {
    String parsed;
    try {
        parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    } catch (UnsupportedEncodingException e) {
        parsed = new String(response.data);
    }
    return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

可以看到,Request 定義了一個(gè)網(wǎng)絡(luò)請(qǐng)求的細(xì)節(jié)問題,服務(wù)、參數(shù)、重試策略、緩存策略、返回?cái)?shù)據(jù)處理等。其中 Header、參數(shù)、返回值類型的變化可以通過繼承 Request,實(shí)現(xiàn)不同的子類來實(shí)現(xiàn)。在 volley 中內(nèi)置了一些實(shí)現(xiàn),包括 StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest等,繼承的方式顯得不太靈活,需要定義一堆的 Request。我們可以稍作擴(kuò)展,用組合來避免 Request 類的爆炸。

public interface ResponseParser {
    Response parseNetworkResponse(NetworkResponse networkResponse);
}

public  class MyRequest extends Request {

    final private Map params;
    final private Map headers;
    final private ResponseParser responseParser;
    final private Response.Listener listener;

    public MyRequest(int method,
                     String url,
                     Response.ErrorListener errorListener,
                     Map params,
                     Map headers,
                     ResponseParser responseParser,
                     Response.Listener listener) {
        super(method, url, errorListener);
        this.params = params;
        this.headers = headers;
        this.responseParser = responseParser;
        this.listener = listener;
    }

    @Override
    protected Response parseNetworkResponse(NetworkResponse networkResponse) {
        return responseParser.parseNetworkResponse(networkResponse);
    }

    @Override
    protected void deliverResponse(T t) {
        listener.onResponse(t);
    }

    @Override
    public Map getHeaders() throws AuthFailureError {
        return headers;
    }

    @Override
    protected Map getParams() throws AuthFailureError {
        return params;
    }
}

在使用 volley 的過程中,我們大部分時(shí)間會(huì)是處理 Request 相關(guān)的內(nèi)容。將 Request 設(shè)計(jì)的簡(jiǎn)單直白,降低使用者的難度,這是 volley 非常友好的地方,同時(shí),Volley 的 Request 創(chuàng)建過程也許并不是太有趣,比如請(qǐng)求參數(shù)的創(chuàng)建,無(wú)聊且不太直觀。根據(jù)具體的業(yè)務(wù),寫一些 Builder 可以一定程度彌補(bǔ)這一點(diǎn)。接下來是 RequestQueue 的部分,RequestQueue 在邏輯上是動(dòng)態(tài)的,是一臺(tái)機(jī)器運(yùn)轉(zhuǎn)的部分,稍微復(fù)雜一些,但是,在某種意義上它又是穩(wěn)定的,它提供了很好的默認(rèn)實(shí)現(xiàn),多數(shù)情況下我們不需要定制,不需要擴(kuò)展,不需要理解具體細(xì)節(jié),它在那里,它完美的 work。

RequestQueue

RequestQueue 這臺(tái)機(jī)器是如何運(yùn)轉(zhuǎn)的呢?使用 add 方法,一個(gè) Request 首先會(huì)被放入 mCacheQueue,mCacheQueue 是一個(gè) PriorityBlockingQueue,CacheDispatchermCacheQueue 中取出 Request,查看 cache 中是否已經(jīng)有相同之前相同的請(qǐng)求得到的有效緩存,有就直接使用 ResponseDelivery 將緩存的結(jié)果分發(fā)到 Request 中的 deliverResponse 方法,從而傳遞到用戶手中;如果沒有有效的緩存結(jié)果,則將 Request 添加到 mNetworkQueue,NetworkDispatchermNetworkQueue 中讀取 Request,使用 Network 執(zhí)行 Request 中的請(qǐng)求,成功后將結(jié)果放入 Cache,同時(shí)用 ResponseDelivery 分發(fā)結(jié)果。這就是一個(gè) Request 的周期。
流程圖如下:
上面我們提到了一些關(guān)鍵的類或?qū)傩浴?/p>

mCacheQueue、mNetworkQueue:兩個(gè)都是 PriorityBlockingQueue,結(jié)合 Request 實(shí)現(xiàn)的 Comparable 接口,Request 的優(yōu)先級(jí)在這里實(shí)現(xiàn)。

CacheDispatcher、NetworkDispatcher:負(fù)責(zé)從 Request 到 Response 的轉(zhuǎn)化

Cache、Network:將 Request 轉(zhuǎn)化為 Response 的實(shí)際執(zhí)行者

ResponseDelivery:分發(fā) Response

有了這些概念后,我們分別分析這些類的代碼,試著由低向上來理解 RequestQueue。

CacheDispatcher
public class CacheDispatcher extends Thread {
    
    public CacheDispatcher(
            BlockingQueue> cacheQueue, BlockingQueue> networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }
    
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
        while (true) {
            try {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request request = mCacheQueue.take();
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    mNetworkQueue.put(request);
                    continue;
                }
                Response response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                mDelivery.postResponse(request, response);
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }
}

為便于理解,刪去了大量代碼,只保留下核心部分。

在構(gòu)造方法中,我們看到了 mCacheQueue,Request 在 RequestQueue 類中會(huì)先放到這里;還有mNetworkQueue,mCacheQueue 中的 Request 沒有命中 Cache,會(huì)被再放到這里;mCache,存取 Response 的地方;mDelivery,Response 被分發(fā)的地方。

CacheDispatcher 繼承了 Thread,我們可以 quit 它,看到 quit 中先標(biāo)記,然后 interrupt,意味著 run 方法中會(huì)相應(yīng)的處理。

run 中,在線程中開啟了無(wú)限循環(huán),每次循環(huán)從 mCacheQueue 讀取 Request,然后進(jìn)行 1 中提到的過程。也可以看到對(duì)于 quit 的處理。

NetworkDispatcher
public class NetworkDispatcher extends Thread {
    public NetworkDispatcher(BlockingQueue> queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
        while (true) {
            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;
            }
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                Response response = request.parseNetworkResponse(networkResponse);.
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                }
                mDelivery.postResponse(request, response);
        }
    }
}

NetworkDispatcher 和 CacheDispatcher 是不是非常相似?

Cache

一個(gè)接口類,和普通的 Cache 沒有什么太大差別,不贅述。

Network

public interface Network {
    public NetworkResponse performRequest(Request request) throws VolleyError;
}

public interface HttpStack {
    public HttpResponse performRequest(Request request, Map additionalHeaders)
        throws IOException, AuthFailureError;
}

它的實(shí)現(xiàn)是 BasicNetwork,BasicNetwork 依賴 HttpStack,HttpStack 是實(shí)際發(fā)起網(wǎng)絡(luò)請(qǐng)求的地方,volley 分別提供了 HttpClient 和 HttpURLConnection 的實(shí)現(xiàn)。我們可以定制這部分,比如基于 OKHttp 的實(shí)現(xiàn)。在最后看 Volley 類源碼時(shí)我們可以看到這個(gè)過程。

ResponseDelivery

ResponseDelivery 負(fù)責(zé)分發(fā) Response,對(duì) Android 來說,就是通過 Handler 將 Response 發(fā)送到了主線程

public class ExecutorDelivery implements ResponseDelivery {
    private final Executor mResponsePoster;

    public ExecutorDelivery(final Handler handler) {
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }
    @Override
    public void postResponse(Request request, Response response, Runnable runnable) {
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }
}

在 ResponseDeliveryRunnable 中調(diào)用了 Request.deliverResponse。

RequestQueue

有了上面的內(nèi)容,理解 RequestQueue 就很容易了。首先看構(gòu)造方法。

    public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;
        mNetwork = network;
        mDispatchers = new NetworkDispatcher[threadPoolSize];
        mDelivery = delivery;
    }

一共四個(gè)參數(shù)。

cache:一個(gè)實(shí)現(xiàn)了讀寫刪功能的 Cache 對(duì)象,通過它我們可以實(shí)現(xiàn)自己的緩存策略。

Network:前面提到,這是一個(gè)接口,只有一個(gè)要實(shí)現(xiàn)的方法,傳入一個(gè) Request,傳出一個(gè) Response?;蛟S我們也可以把緩存策略放到這里面來,讓 Cache 對(duì) RequestQueue 透明,也未嘗不是一個(gè)不錯(cuò)的想法。

threadPoolSize:NetworkDispatcher 的個(gè)數(shù),決定了網(wǎng)絡(luò)請(qǐng)求的最大并發(fā)數(shù)。

ResponseDelivery:分發(fā) Response,默認(rèn)實(shí)現(xiàn) ExecutorDelivery 將 Response 分發(fā)到 Request.deliverResponse, 這就是我們 override deliverResponse 方法可以得到 Response 的原因,同時(shí)我們從上面 ExecutorDelivery 的代碼中看到,這個(gè)分發(fā)是通過像一個(gè) Handler post 一個(gè) Runnable 實(shí)現(xiàn)的,Handler 的性質(zhì)決定了數(shù)據(jù)分發(fā)到哪個(gè)線程。

下面看 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();
        }
    }

mCacheDispatcher、mDispatchers 初始化,從上面我們知道它們都是 Thread 的子類, start 后內(nèi)部開始循環(huán),從 mCacheQueue、mNetworkQueue 讀取 Request 進(jìn)行處理。

接下來是最重要的方法 add。
    public  Request add(Request 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> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList>();
                }
                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;
        }
    }

和我們開始討論 RequestQueue 時(shí)的流程圖多了一些細(xì)節(jié)的處理,比如如果一個(gè) Request 被設(shè)置為不可緩存,則直接添加到 mNetworkQueue;有相等的 Request 時(shí),會(huì)把重復(fù)的請(qǐng)求加到一個(gè)等待隊(duì)列,分享請(qǐng)求結(jié)果。

RequestQueue 的內(nèi)容就到這里了。RequestQueue 在并發(fā)上沒有選擇使用 Java 提供的線程池,而是使用固定數(shù)據(jù)的線程數(shù)組(NetworkDispatcher)配合阻塞的優(yōu)先級(jí)隊(duì)列來實(shí)現(xiàn)任務(wù)并發(fā)。同時(shí)在緩存、網(wǎng)絡(luò)、分發(fā)方式上提供了可定制的接口,當(dāng)然,多數(shù)時(shí)候使用默認(rèn)實(shí)現(xiàn)就可以了。為了方便的使用默認(rèn)的實(shí)現(xiàn),就像文章開始的 demo 展示的那樣,volley 提供了一個(gè)工具類 Volley。

Volley

Volley 只提供兩個(gè)方法。用來創(chuàng)建默認(rèn)的 RequestQueue,如下:

public class 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) {
            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;
    }

    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }
}
擴(kuò)展

Volley 自帶的 NetworkImageView

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66937.html

相關(guān)文章

  • Android網(wǎng)絡(luò)編程3之Volley用法全解析

    摘要:前言想必很多人都用過,為了建立網(wǎng)絡(luò)編程的知識(shí)體系,是必須要講的知識(shí)點(diǎn),所以我這里有必要再次介紹一下的使用。簡(jiǎn)介在年大會(huì)上推出了一個(gè)新的網(wǎng)絡(luò)通信框架。在使用前請(qǐng)下載庫(kù)并放在目錄下并到工程中。 前言 Volley想必很多人都用過,為了建立網(wǎng)絡(luò)編程的知識(shí)體系,Volley是必須要講的知識(shí)點(diǎn),所以我這里有必要再次介紹一下Volley的使用。 1.Volley簡(jiǎn)介 在2013年Google I/...

    Code4App 評(píng)論0 收藏0
  • Android網(wǎng)絡(luò)編程4之從源碼解析Volley

    摘要:接下來看看網(wǎng)絡(luò)調(diào)度線程。讓我們?cè)倩氐?,?qǐng)求網(wǎng)絡(luò)后,會(huì)將響應(yīng)結(jié)果存在緩存中,如果響應(yīng)結(jié)果成功則調(diào)用來回調(diào)給主線程。我們用請(qǐng)求網(wǎng)絡(luò)的寫法是這樣的將請(qǐng)求添加在請(qǐng)求隊(duì)列中看到第行整個(gè)的大致流程都通了吧,好了關(guān)于的源碼就講到這里。 1.Volley結(jié)構(gòu)圖 showImg(https://segmentfault.com/img/remote/1460000011351317); 從上圖可以看到V...

    fevin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

txgcwm

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<