Análisis de principio de Volley

prefacio

En el proceso de desarrollo y programación de Android, es inevitable involucrar la programación en red, lo que requiere que nuestros desarrolladores sean más competentes en la programación en red. Jaja, ¡comencemos analizando Volley hoy!

1.0 ¿Qué es Voleibol?

Volley es una biblioteca HTTP lanzada en la conferencia de E/S de Googel en 2013, que puede ayudar a las aplicaciones de Android a realizar solicitudes de red más fácilmente. No solo puede acceder a la red para obtener datos, sino también acceder a la red para obtener imágenes.

Ventajas y desventajas de 2.0 Volley

Volley tiene las siguientes ventajas:
  • Enviar solicitudes de red automáticamente
  • Alta conexión de red concurrente
  • Respuestas transparentes a la memoria almacenadas en caché en el disco a través de la coherencia de caché HTTP estándar
  • Soporte para especificar la prioridad de las solicitudes
  • Cancele la API de solicitudes o especifique una región en la cola de solicitudes de cancelación
  • Los marcos son fácilmente personalizables. Por ejemplo, funciones personalizadas de reintento o devolución de llamada
  • La ordenación estricta puede facilitar la carga asincrónica de datos de red y mostrarlos correctamente en la interfaz de usuario
  • Incluye herramientas de depuración y rastreo.
Desventajas de Volley:
  • No apto para descargar archivos de datos de gran tamaño

Cola de solicitud de red de 3.0 Volley

El proceso de usar Volley es crear un RequestQueueobjeto (cola de solicitudes) y luego Requestenviarle (solicitudes).

// 代码[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);

La lógica del código anterior es principalmente construir una StringRequestinstancia de y luego agregar esta instancia a la cola de solicitudes RequestQueue. Veamos Volley.newRequestQueue(Context)el código fuente del método en la 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 se puede ver en el código fuente anterior, newRequestQueue(Context)es newRequestQueue(Context, HttpStack)el método sobrecargado de . A continuación, observamos principalmente newRequestQueue(Context, HttpStack)el método. En la Nota 1, new File(File, String)se construye un caché (este es un sustantivo) a través de la inicialización cacheDir. En la Nota 3, new DiskBasedCache(cacheDir)se asignan 5M para este caché. Espacio de almacenamiento;
de vuelta a la Nota 2, cuando la versión SDK es mayor o igual a 9, es decir, el número de versión de Android es mayor o igual a 2.3, luego cree en base a, de lo contrario, cree en base a la solicitud HttpURLConnectionde HurlStackejecución HttpClient, HttpClientStacky luego en la Nota 3, al new RequestQueue(Cache, Network)crear una cola de solicitudes, veamos new RequestQueue(Cache, Network)el código fuente 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 construcción new RequestQueue(Cache, Network)Asigne 4 subprocesos para solicitudes de red. Para RequestQueueesta clase, la función principal es servir como grupo de subprocesos para enviar solicitudes en la cola: al llamar, la add(Request)solicitud entrante (Solicitud) se analizará en la cola de caché (caché) o en la cola de red (red), y luego se pasará volver al hilo principal, es decir, la función de devolución de llamada en el código [1] onResponse(String)y onErrorResponse(VolleyError)obtiene el contenido de la solicitud de la devolución de llamada;
en el comentario 4 del código [1], se llama al método add(Request):

// 源码[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 se puede ver en el código fuente del add(Request)método anterior, la lógica principal es: si la solicitud (solicitud) no se puede almacenar en caché, se agregará directamente a la cola de caché; de lo contrario, se agregará a la cola de caché; después de obtener y , devolver la solicitud y mNetworkQueuellamar mCacheQueueal start()método (como la Nota 4 del código fuente [2]), al visualizar start()el código fuente del 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();
        }
    }

Principalmente crea CacheDispatcher(BlockingQueue<Request<?>>, BlockingQueue<Request<?>>, Cache, ResponseDelivery)una instancia de e mCacheDispatcher.start()inicia el subproceso de programación de caché a través de , y crea NetworkDispatcher(BlockingQueue<Request<?>>,Network, Cache,ResponseDelivery)una instancia de e networkDispatcher.start()inicia el subproceso de programación de red a través de .

4.0 Subproceso de despachador de red NetworkDispatcher

El subproceso de programación de red NetworkDispatcheres un Threadsubproceso heredado de, al ver run()el código fuente de su método de tarea:

// 源码[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) {
                ...

            }
        }
    }

La lógica principal de la programación de red es juzgar primero si la solicitud de red ha sido cancelada. De lo contrario, mNetwork.performRequest(request)ejecutar la respuesta de red de solicitud. Después de obtener la respuesta, primero almacenarla en caché localmente y luego volver a llamar al subproceso principal.

Subproceso de programación de caché 5.0 CacheDispatcher

El código fuente del hilo de programación de caché CacheDispatcheres el siguiente:

@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) {
                ...
            }
        }
    }

Del mismo modo, si la solicitud no se cancela, se recupera la memoria caché local. Cuando la memoria caché local no existe, se pierde o ha caducado, la solicitud se agrega a la cola de solicitudes de la red. Cuando se alcanza la memoria caché local, la respuesta almacenada en memoria caché se vuelve a llamar al subproceso principal;

6.0 Análisis del principio de volea

Para enviar una solicitud, solo necesita crear una solicitud y add()agregarla a través del método RequestQueue. Una vez que se agrega la solicitud, pasa por la cola, a través de una serie de envíos y luego recupera los datos de respuesta sin procesar.

Cuando se ejecuta el método add(), Volley desencadena la ejecución de un subproceso del controlador de caché y una serie de subprocesos del controlador de red. Al agregar una solicitud a la cola, el subproceso de caché la capturará y la activará: si la caché puede procesar la solicitud, el análisis de los datos de respuesta se realizará en el subproceso de caché y se devolverá al subproceso principal. Si la solicitud no puede ser manejada por el caché, se coloca en una cola de red. El primer subproceso de red disponible en el grupo de subprocesos de red obtendrá la solicitud de la cola y ejecutará la operación HTTP, analizará los datos de respuesta del subproceso de trabajo, escribirá los datos en el caché y devolverá los datos analizados al subproceso principal.

El ciclo de vida de una solicitud

El ciclo de vida de una solicitud

resumen

En este punto, el análisis de la biblioteca Volley ha terminado temporalmente. Esta vez, el autor analiza principalmente el proceso desde que se agrega una solicitud a la cola de solicitudes hasta que devuelve una respuesta, y comprende cómo Volley programa una solicitud y luego obtiene una respuesta y la devuelve. Jaja gracias por leer...


Descargar código fuente TestVolley

Supongo que te gusta

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