Análise do Princípio do Volley

prefácio

No processo de desenvolvimento e programação do Android, é inevitável envolver programação de rede, o que exige que nossos desenvolvedores sejam mais proficientes em programação de rede. Haha, vamos começar analisando o Volley hoje!

1.0 O que é Voleibol

Volley é uma biblioteca HTTP lançada na conferência Googel I/O em 2013, que pode ajudar aplicativos Android a realizar solicitações de rede com mais facilidade. Ele pode não apenas acessar a rede para obter dados, mas também acessar a rede para obter fotos.

Vantagens e desvantagens do vôlei 2.0

O vôlei tem as seguintes vantagens:
  • Despachar solicitações de rede automaticamente
  • Alta conexão de rede simultânea
  • Respostas transparentes à memória armazenadas em cache no disco via coerência de cache HTTP padrão
  • Suporte para especificar a prioridade das solicitações
  • API de solicitação de cancelamento ou especifique uma região na fila de solicitação de cancelamento
  • As estruturas são facilmente personalizáveis. Por exemplo, funções personalizadas de repetição ou retorno de chamada
  • A ordenação forte pode facilitar o carregamento assíncrono de dados de rede e exibi-los corretamente na IU
  • Inclui ferramentas de depuração e rastreamento
Desvantagens do Volley:
  • Não é adequado para baixar arquivos de dados grandes

3.0 Fila de solicitação de rede do Volley

O processo de uso do Volley é criar um RequestQueueobjeto (fila de solicitações) e, em seguida, Requestenviar (solicitações) a ele.

// 代码[1]

final TextView textView = (TextView) MainActivity.this.findViewById(R.id.tv);

//[1.0]创建 RequestQueue 的实例
RequestQueue requestQueue = Volley.newRequestQueue(MainActivity.this); // 1

String url = "http://gank.io/api/data/Android/10/1";

//[2.0]构造一个 request(请求)
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        textView.setText("Response is: " + response.toString());
    }
}, new Response.ErrorListener() {

    @Override
    public void onErrorResponse(VolleyError error) {
        textView.setText("Error is happenning");
    }
});

//[3.0]把 request 添加进请求队列 RequestQueue 里面
requestQueue.add(request);

A lógica do código acima é principalmente construir uma StringRequestinstância e, em seguida, adicionar essa instância à fila de solicitações RequestQueue. Vejamos Volley.newRequestQueue(Context)o código-fonte do método na Nota 1:

// 源码[2]

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);  // 1

        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) {  // 2
                stack = new HurlStack();
            } else {
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network); // 3
        queue.start(); // 4

        return queue;
    }

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

Como pode ser visto no código-fonte acima, newRequestQueue(Context)é newRequestQueue(Context, HttpStack)o método sobrecarregado de . Em seguida, examinamos principalmente newRequestQueue(Context, HttpStack)o método. Na Nota 1, new File(File, String)um cache (este é um substantivo) é construído por meio da inicialização cacheDir. Na Nota 3, new DiskBasedCache(cacheDir)5M é alocado para este cache. Espaço de armazenamento;
de volta à Nota 2, quando a versão do SDK for maior ou igual a 9, ou seja, o número da versão do Android for maior ou igual a 2,3, crie com base em , caso contrário, crie com base na solicitação HttpURLConnectionde HurlStackexecução HttpClient, HttpClientStacke então na Nota 3, Ao new RequestQueue(Cache, Network)criar uma fila de solicitações, vejamos new RequestQueue(Cache, Network)o código-fonte de:

// 源码[3]

    ...

private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    ...

public RequestQueue(Cache cache, Network network) {
        this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
    }
    ...

Método de construção new RequestQueue(Cache, Network)Aloca 4 threads para solicitações de rede. Para RequestQueueesta classe, a função principal é servir como pool de threads para despachar requisições na fila: ao chamar, a add(Request)requisição recebida (Request) será analisada na fila de cache (cache) ou na fila de rede (rede), e então passada de volta ao thread principal, ou seja, a função de callback no código [1] onResponse(String)e onErrorResponse(VolleyError)obtém o conteúdo da solicitação do callback;
no comentário 4 do código [1], add(Request)o método é chamado:

// 源码[4]

public <T> Request<T> add(Request<T> request) {
        request.setRequestQueue(this);
        //同步代码块,保证 mCurrentRequests.add(request) 在一个进程里面的所有线程里面,有且只能在
        //一个线程里面执行
        synchronized (mCurrentRequests) { 
            mCurrentRequests.add(request);
        }

        request.setSequence(getSequenceNumber());
        request.addMarker("add-to-queue");

        //如果不可以存储,就把请求(request)添加进网络调度队列里面
        if (!request.shouldCache()) {
            mNetworkQueue.add(request);  
            return request;
        }

        //如果可以存储:
        //同步代码块
        synchronized (mWaitingRequests) {  
            String cacheKey = request.getCacheKey();
            //如果之前有相同的请求并且还没有返回结果的,就把此请求加入到 mWaitingRequests 里面
            if (mWaitingRequests.containsKey(cacheKey)) {   
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                //如果没有请求在进行中,重新初始化 Queue<Request<?>> 的实例
                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 {
                //如果没有的话,就把请求添加到 mCacheQueue 队列里面
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);  
            }
            return request;
        }
    }
}

Como pode ser visto no código-fonte do add(Request)método acima, a lógica principal é: se a requisição (pedido) não puder ser armazenada em cache, ela será adicionada diretamente à fila de cache, caso contrário, será adicionada à fila de cache; após obter e , retorne a requisição e mNetworkQueuechame mCacheQueueo start()método (como Nota 4 do código fonte [2]), ao visualizar start()o código fonte do método:

// 源码[5]

public void start() {
        stop();  // 确保当前的缓存调度和网络调度都已经停止
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

Ele cria principalmente CacheDispatcher(BlockingQueue<Request<?>>, BlockingQueue<Request<?>>, Cache, ResponseDelivery)uma instância e mCacheDispatcher.start()inicia o encadeamento de agendamento de cache por meio de , e cria NetworkDispatcher(BlockingQueue<Request<?>>,Network, Cache,ResponseDelivery)uma instância de e networkDispatcher.start()inicia o encadeamento de agendamento de rede por meio de .

4.0 Encadeamento do despachante de rede NetworkDispatcher

O encadeamento de agendamento de rede NetworkDispatcheré um Threadencadeamento herdado de , visualizando run()o código-fonte de seu método de tarefa:

// 源码[6]

@Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {
            try {  
                // 从网络队列中取出请求      
                request = mQueue.take();
            } catch (InterruptedException e) {           
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");

                // 如果请求被取消
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");

                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // 把网络请求到的实体缓存到 源码[2] 的注释 1 处的本地缓存文件里面
                if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                request.markDelivered();
                // 回调请求到的响应给主线程
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                ...

            } catch (Exception e) {
                ...

            }
        }
    }

A lógica principal do escalonamento de rede é primeiro julgar se a solicitação de rede foi cancelada. Caso contrário, mNetwork.performRequest(request)execute a resposta da rede de solicitação. Depois de obter a resposta, primeiro armazene-a localmente e, em seguida, chame de volta para o thread principal.

Encadeamento de agendamento de cache 5.0 CacheDispatcher

O código-fonte do encadeamento de agendamento de cache CacheDispatcheré o seguinte:

@Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        mCache.initialize();

        while (true) {
            try {            
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                //如果请求被取消
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // 尝试在本地缓存中取回数据
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    //本地缓存丢失或者没有
                    request.addMarker("cache-miss");                   
                    mNetworkQueue.put(request);
                    continue;
                }

                // 本地缓存过期
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // 命中缓存
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // 回调相应给主线程
                    mDelivery.postResponse(request, response);
                } else {

                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    response.intermediate = true;

                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                ...
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                ...
            }
        }
    }

Da mesma forma, se a solicitação não for cancelada, o cache local é recuperado. Quando o cache local não existe, é perdido ou expirou, a solicitação é adicionada à fila de solicitações de rede. Quando o cache local é atingido, a resposta em cache é chamado de volta para o thread principal;

6.0 Análise do princípio do Volley

Para enviar uma solicitação, você só precisa construir uma solicitação e add()adicioná-la por meio do método RequestQueue. Depois que a solicitação é adicionada, ela passa pela fila, por uma série de despachos e, em seguida, recupera os dados brutos da resposta.

Quando o método é executado add(), o Volley aciona a execução de um thread do manipulador de cache e uma série de threads do manipulador de rede. Ao adicionar uma solicitação à fila, ela será capturada e acionada pelo thread do cache: Se a solicitação puder ser processada pelo cache, a análise dos dados da resposta será realizada no thread do cache e retornada ao thread principal. Se a solicitação não puder ser tratada pelo cache, ela será colocada em uma fila de rede. O primeiro thread de rede disponível no pool de threads de rede obterá a solicitação da fila e executará a operação HTTP, analisará os dados de resposta do thread de trabalho, gravará os dados no cache e retornará os dados analisados ​​ao thread principal.

O ciclo de vida de uma solicitação

O ciclo de vida de uma solicitação

resumo

Neste ponto, a análise da biblioteca Volley está temporariamente encerrada. Desta vez, o autor analisa principalmente o processo desde quando uma solicitação é adicionada à fila de solicitações até quando ela retorna uma resposta e entende como o Volley agenda uma solicitação e, em seguida, obtém uma resposta e a retorna. haha obrigada por ler...


Download do código-fonte TestVolley

Acho que você gosta

Origin blog.csdn.net/HongHua_bai/article/details/78308331
Recomendado
Clasificación