El concepto básico de la explicación del principio OKHttp

Prefacio:

1.1 ¿Por qué escribir este artículo?

OKHttp es un marco de comunicación de código abierto producido por Square. En la actualidad, la mayoría de las aplicaciones en el mercado usan este marco, entonces, ¿por qué tantas aplicaciones eligen usar este marco? Algunas personas dicen que si otros me usan, yo lo usaré, pero eso definitivamente no es cierto. Este artículo lo llevará a comprender por qué usamos OKHttp y el principio de OKHttp.

1.2 Dirección del proyecto OKHttp:

https://github.com/square/okhttp

1.3 Serie Introducción:

El proyecto OKHttp en sí todavía es muy grande e involucra muchos puntos de conocimiento, por lo que es difícil resumir todo el artículo en un solo artículo, por lo que voy a escribir una serie de artículos para explicar OKHttp en detalle.

Prepárate para escribir lo siguiente.

El concepto básico de la explicación del principio OKHttp (se espera que se publique en 03.15)

Modo de cadena de responsabilidad y extensión de la explicación del principio OKHttp (se espera que se publique el 18 de marzo)

Interceptor de reintento explicado por el principio OKHttp (se espera que se publique el 22 de marzo)

Interceptor de enrutamiento explicado por el principio OKHttp

Interceptor de caché explicado por el principio OKHttp

Interceptor de grupo de conexiones explicado por el principio OKHttp

Interceptor de solicitudes explicado por el principio OKHttp

PD: Se espera que el progreso de 1-2 artículos se actualice cada semana.

1.4 La primera parte de la serie OKHttp

Este artículo explica principalmente las siguientes partes:

1. Uso sencillo de OKHttp

2. Varios conceptos principales de OKHttp

3. El proceso básico de OKHttp enviando una solicitud

4. Gestión de subprocesos en el programador

5. ¿Por qué elegir OKHttp?

1. Varios conceptos principales de OKHttp

PD: Los siguientes nombres chinos están tomados según mi propio entendimiento, no son nombres oficiales.

1. Centro de configuración, OKHttpClient

OKHttp es un marco altamente personalizable. Proporciona muchos elementos de funciones configurables, que los desarrolladores pueden configurar de acuerdo con sus propias elecciones. Como Dns, Proxy, connectTimeout, etc.

Y OKHttpClient es un contenedor de estas configuraciones.Utiliza el modo constructor para crear, y los desarrolladores pueden elegir según sus propias necesidades.

2. Cuerpo de la solicitud, RealCall

Para cada solicitud, OKHttp encapsula una RealCall para ejecutar el proceso de solicitud específico. Entonces, naturalmente, la cadena de responsabilidad de cinco capas también es creada e invocada por ella.

3. Programador, Despachador

El programador es responsable de la ejecución específica de cada cuerpo de solicitud. Entonces, naturalmente, el planificador contiene un grupo de subprocesos de solicitud, así como colas de solicitud, colas de ejecución, colas de espera, etc. 

4. Cadena de responsabilidad, List<Interceptor>

OKHttp contiene cinco capas de cadena de responsabilidad por defecto, que son

RetryAndFollowUpInterceptor: reintentar el interceptor de seguimiento

BridgeInterceptor: Interceptor de puente

CacheInterceptor: interceptor de caché

ConnectInterceptor: interceptor de grupo de conexiones

CallServerInterceptor: interceptor de solicitudes

El proceso completo de envío de una solicitud se ejecutará secuencialmente de arriba a abajo.Si un interceptor juzga que la ejecución está completa, el resultado final se devolverá capa por capa.

Por lo tanto, el interceptor de cinco capas también es el núcleo de OKHttp, y habrá capítulos especiales para explicarlos uno por uno.

5. Solicitud, Solicitud

Como su nombre indica, contiene toda la información necesaria para la solicitud.

6. Respuesta, Respuesta

Como su nombre lo indica, contiene toda la información necesaria para la respuesta.

En segundo lugar , el uso básico de OKHttp

Este no es el enfoque de este artículo, solo una breve introducción a cómo usarlo.

2.1 Configurar OKHttpClient

Aquí solo hice una configuración y configuré un directorio de caché.

 val builder = OkHttpClient.Builder()
 builder.cache(Cache(File(context?.filesDir?.absolutePath + File.separator + "ok"), 100))
 val client = builder.build()

2.2 enviar la solicitud,

Hay dos tipos de solicitudes, solicitudes sincrónicas y solicitudes asincrónicas. Las solicitudes sincrónicas deben ejecutarse en subprocesos secundarios.

La primera forma de sincronizar:

        val builder = Request.Builder()
        val request = builder.url("https://www.baidu.com").build()
        val newCall = client.newCall(request)
            service.execute {
                val response = newCall.execute()
            }
        

La segunda forma asíncrona:

            val builder = Request.Builder()
            val request = builder.url("https://www.baidu.com").build()
            val newCall = client.newCall(request)
            newCall.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    logI(call.toString());
                }

                override fun onResponse(call: Call, response: Response) {
                
                }

            })

2.3 Manejo de valores devueltos

La respuesta contiene el flujo de datos devuelto, solo necesitamos serializar la matriz de datos devueltos.

 val content = IOHelper.readStrByCode(response.body()?.byteStream(), "utf-8")
 logI(content)

El método readStrByCode está vinculado para convertir la matriz en una cadena.

https://github.com/aa5279aa/android_all_demo/blob/master/DemoClient/app/src/main/java/com/xt/client/util/IOHelper.java

3. El proceso principal de envío de OKHttp

Las solicitudes asíncronas y síncronas tienen un proceso similar, excepto que las solicitudes síncronas devuelven la respuesta directamente, mientras que las solicitudes asíncronas se notifican a través de devoluciones de llamada. Aquí tomamos una solicitud asíncrona como ejemplo para explicar todo el proceso de envío de solicitudes.

3.1 Construyendo el cuerpo de la solicitud RealCall

El RealCall construido aquí incluye principalmente la solicitud original y el monitoreo de eventos.

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

3.2 Iniciar el proceso de solicitud RealCall.enqueue()

Hay dos cosas principales que hacer aquí,

Lo primero es volver a llamar al observador.

Lo segundo es entregar al programador la ejecución del método de ejecución de AsyncCall en el subproceso.

public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

3.3 Ejecutar el proceso de solicitud ejecutar()

Primero llame a la cadena de responsabilidad para obtener la respuesta final.

Luego notifique callBack para realizar la devolución de llamada correspondiente.

Finalmente complete la solicitud a través del programador

protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

Cuarto , la gestión de subprocesos en el planificador.

Como se mencionó anteriormente, el programador es responsable de distribuir las tareas de solicitud una por una a los subprocesos para su administración. Entonces, ¿cómo se gestionan los hilos?

4.1 El método enqueue() en Dispatcher

4.1.1 Método de bloqueo:

Este método está bloqueado para evitar conflictos al agregar tareas de varios subprocesos.

4.1.2 Juzgar si la cantidad excede el estándar

Aquí se juzga si el número de solicitudes que se ejecutan es mayor que 64 y si el número de solicitudes para un solo enlace es mayor que 5 (64 y 5 se pueden configurar aquí, y un solo enlace se refiere a la dirección de la interfaz de solicitud es mismo), si excede, se pondrá en cola; de lo contrario, se puede ejecutar de inmediato.

synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

4.1.3 Obtener y crear un grupo de subprocesos

1. Determinar si hay un grupo de subprocesos

Si no existe, se crea el grupo de subprocesos. de lo contrario, devuélvelo directamente

Aquí hay una extensión de una pequeña pregunta, ¿por qué crea un grupo de subprocesos cuando lo usa? Supongo que es por el rendimiento, la creación temprana de un grupo de subprocesos no desperdicia el rendimiento.

2. Crear un grupo de subprocesos

Los parámetros de configuración del grupo de subprocesos utilizados aquí, la cantidad de subprocesos principales es 0, la cantidad máxima de subprocesos es MAX_VALUE y el tiempo de supervivencia de los subprocesos no centrales es de 60 segundos.

La cola usa SynchronousQueue, que es adecuada para el modelo productor-consumidor. El SynchronousQueue específico puede ser Baidu por sí mismo y no se expandirá aquí.

public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

3. Por qué no hay problemas de rendimiento

Vemos aquí que la cantidad de grupos de subprocesos de solicitud OKHttp no está configurada en línea, por lo que no habrá problemas de rendimiento. De hecho, debido al límite anterior de maxRequest, la cantidad de subprocesos no alcanzará realmente el nivel de MAX_VALUE.

Entonces, puede comprender la estrategia del grupo de subprocesos OK de esta manera, y al mismo tiempo con cada solicitud, luego cada solicitud es procesada por un subproceso separado. Si se completa una solicitud, el subproceso ocupado por ella puede liberarse para su uso en solicitudes posteriores.

4. Estrategia de grupo de subprocesos personalizado

Incluso si el número máximo de solicitudes se ha limitado a 64, para algunos teléfonos móviles con bajo rendimiento, puede resultar difícil realizar grandes solicitudes simultáneas. Así que no importa, una de las características de OK es su gran capacidad de configuración. No importa, podemos personalizar el grupo de subprocesos entrantes y dejar que OK use nuestro grupo de subprocesos personalizado.

Podemos pasar un grupo de subprocesos personalizado a través del constructor parametrizado de Dispatcher.

            val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
            val dispatcher = Dispatcher(newSingleThreadExecutor)
            val builder1 = OkHttpClient.Builder()
            builder1.dispatcher(dispatcher);
            val build = builder.build()

4.1.4 Llamada de programación de grupo de subprocesos Llamada

1. Ejecute AsyncCall

Después de obtener el grupo de subprocesos, se puede transferir a su tarea de llamada final para su ejecución. eventualmente llamará a AsyncCall

el método de ejecución.

 executorService().execute(call);

2. ¿Por qué se ejecuta el método de ejecución de AsyncCall?

AsyncCall hereda de NameRunnable, que hereda de Runnable. La implementación en NameRunnable es la siguiente, por lo que se ejecutará el método de ejecución de NameRunnable.

public abstract class NamedRunnable implements Runnable {
  protected final String name;

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

Cinco , ¿por qué elegir usar OKHttp?

respuesta regular

Esta es una pregunta que a menudo se hace en las entrevistas y que suelo hacer cuando entrevisto a otros. La respuesta habitual es la siguiente:

1. Todo el mundo lo usa, así que yo también lo uso.

2. Los de grandes fábricas tienen buena estabilidad y están más seguros.

3. Fácil de usar, más cómodo de usar.

4. En mantenimiento continuo, no se preocupe de que nadie resuelva el problema

respuesta mas apropiada

Pero, de hecho, lo que el entrevistador quiere probar es la comprensión de OKHttp, por lo que además de las ventajas anteriores, también podemos hablar de ello desde la perspectiva de las ventajas del marco OKHttp:

1. Alta escalabilidad. De manera similar al almacenamiento en caché, Dns, tiempos de espera de solicitud/conexión/respuesta, etc., se pueden pasar a través de la configuración, e incluso los grupos de subprocesos se pueden configurar de acuerdo con sus propias necesidades.

2. OKHttp utiliza la memoria caché del grupo de conexiones para mejorar la eficiencia de la comunicación.

3. El modo de interceptor de cinco capas de la cadena de responsabilidad, las funciones de cada capa son claras y claras, y se proporcionan dos capas de interceptores extensibles para facilitar la transformación requerida.

4. La estructura jerárquica es clara, lo cual es conveniente para la resolución de problemas.

5. Con el uso completo del modo de observador, se vuelve muy simple ver el estado de la solicitud y monitorear el estado de la solicitud.

6. El marco OKIO se utiliza para el procesamiento de datos, que es más eficiente y seguro.

Y así sucesivamente, a medida que el artículo continúa siendo detallado y agregado gradualmente.

Supongo que te gusta

Origin blog.csdn.net/AA5279AA/article/details/123505544
Recomendado
Clasificación