Codificación de alto rendimiento de Android: la quinta solicitud de red y análisis de datos

Aunque los marcos como okhttp se usan comúnmente en proyectos para solicitudes de red, debido a los requisitos de alto rendimiento del desarrollo, todavía necesitamos analizar algunos puntos de rendimiento de las solicitudes de red, para comprender la codificación general en las solicitudes de red y el tercero. party framework La configuración utiliza una comprensión más profunda.

5.1 Elementos básicos de la codificación de solicitudes de red

Cuando comenzamos a discutir las solicitudes de red, desde el punto de vista del cliente, hay tres aspectos principales involucrados: cliente, solicitud y respuesta. Desde la perspectiva del desarrollo y la codificación, nos centraremos principalmente en varios elementos específicos de la solicitud y la respuesta, como se muestra en la figura a continuación.

Para manejar estos elementos de solicitud y respuesta, Android nos proporciona de forma nativa dos familias principales de API:

l HttpClient: las clases DefaultHttpClient y AndroidHttpClient son las clases principales que se usan para esta implementación de HTTP.

l URLConnection: esta es una API más flexible y eficaz para conectarse a una URL. Puede utilizar diferentes protocolos.

Sin embargo, Android oficialmente ya no recomienda HttpClient y lo eliminó del SDK a partir del nivel de API 23. Aunque algunas bibliotecas de terceros aún pueden brindar soporte, considerando la dirección de actualización posterior del SDK, se recomienda encarecidamente no usarlo.

Por lo tanto, si usa la interfaz de solicitud de red nativa, solo debe usar la familia URLConnection, que también es la tendencia de muchos marcos de código abierto de red.

5.2 Acerca de HttpURLConnection

URLConnection es la clase principal (superclase) de todas las clases que representan conexiones de comunicación entre aplicaciones y URL. Las instancias de esta clase se pueden usar para leer y escribir recursos a los que hacen referencia las URL.

HttpURLConnection es una URLConnection que admite funciones específicas de HTTP y JarConnection es una subclase directa de URLConnection.

Muchos marcos de código abierto proporcionan buenas interfaces de codificación basadas en URLConnection. En algunos escenarios, usamos esta API para personalizar la encapsulación de solicitud de red ligera. Esta sección presentará algunos usos básicos de URLConnection.

5.2.1 Protocolos

Cuando hablamos de la comunicación de red, estamos más interesados ​​en usar el protocolo HTTP para realizar la comunicación de datos de red, pero el uso de URLConnection y sus subclases puede admitir protocolos de datos que incluyen:

l HTTP y HTTPS: HttpUrlConnection es la clase principal de esta familia, utilizada para ejecutar solicitudes http;

l FTP: la clase URLConnection predeterminada proporciona una interfaz de comunicación FTP, que se puede utilizar para realizar la comunicación FTP;

l Archivo: También se puede acceder al sistema de archivos local según el URI usando URLConnection;

l JAR: este protocolo se usa para procesar archivos JAR, que se pueden implementar usando la clase de extensión JarUrlConnection.

5.2.2 Métodos

Los principales métodos de solicitud proporcionados por la clase HttpURLConnection son:

l GET: este es el método de solicitud predeterminado, que se puede usar sin configuraciones especiales;

l POST: al llamar a la interfaz URLConnection.setDoInput(), se puede realizar la solicitud Post;

Se pueden implementar otros métodos de solicitud llamando al método URLConnection.setRequestMethod()

5.2.3 Encabezados

Al prepararnos para enviar una solicitud, es posible que debamos agregar algunos datos de metadatos, o agregar información de usuario y sesión, etc., para permitir que el servidor guíe el estado relacionado del cliente. Los encabezados llevarán esta información en formato de clave/valor, y también pueden leer alguna información modificada por el servidor al responder, como el formato de los datos de respuesta, habilitar la compresión, etc.

El uso de la información del encabezado implica principalmente dos interfaces:

l URLConnection.setRequestProperty()

l URLConnection.getHeaderFields()

5.2.4 Tiempo de espera

URLConnection admite dos tipos de tiempos de espera:

l Tiempo de espera de conexión Tiempo de espera de conexión: establezca el período de tiempo de espera llamando al método URLConnection.setConnectTimeout() Durante el período, el cliente esperará a que la conexión se realice correctamente, si se agota el tiempo de espera de la conexión, se lanzará una excepción SocketTimeoutException;

l Tiempo de espera de lectura Tiempo de espera de lectura: se configura llamando al método URLConnection.setReadTimeout(), que es el límite de tiempo para que Inputstream termine de leer. Si la lectura se agota, también se lanzará una excepción SocketTimeoutException.

El valor predeterminado para ambos es 0, es decir, no hay una configuración de tiempo de espera. Por lo tanto, el mecanismo de tiempo de espera predeterminado será controlado por la propia capa de transporte TCP, y este tiempo suele ser más largo que nuestro rango controlable.

5.2.5 Contenido

Cuando iniciamos una nueva conexión con el servidor, comenzamos a esperar la respuesta. Al llamar al método URLConnection.getContent(), obtendremos el contenido de la respuesta en forma de InputStream; al mismo tiempo, hay varios parámetros de respuesta. y tres encabezados principales de información:

l Longitud del contenido: la longitud del contenido de la respuesta, que se lee llamando al método URLConnection.getContentLength();

l Tipo de contenido: el tipo MIME del contenido de la respuesta se lee llamando a URLConnection.getContentType();

l Codificación de contenido: método de codificación de compresión, se lee llamando al método URLConnection.getContentEncoding().

5.2.6 Compresión

A través del valor leído por la codificación de contenido anterior, conocerá el método de compresión del contenido de la respuesta. Por supuesto, el cliente también puede informar al servidor de su método deseado configurando el campo de encabezado Aceptar codificación.

El servidor no habilita la compresión de forma predeterminada, pero el cliente debe verificar el resultado devuelto a través del método URLConnection.getContentEncoding(); el cliente no se descomprimirá automáticamente, si el flujo de datos está comprimido, debe usar GZIPInputStream en lugar del InputStream predeterminado para leer los datos.

5.2.7 Código de respuesta

El código de retorno de la respuesta determinará los diferentes métodos de procesamiento del cliente.Después de obtener el código llamando al método HttpURLConnection.getResponseCode(), se ejecutarán diferentes respuestas. Los códigos de respuesta suelen incluir los siguientes grupos:

2xx: Éxito, el servidor recibe la solicitud y devuelve los datos;

3xx: redirección, que la capa http completa automáticamente en la mayoría de los casos y no requiere procesamiento adicional, entre ellos, 304 pertenece al lado del servidor sin actualización;

4xx: Ocurrió un error en la solicitud del cliente, que puede deberse a parámetros o sintaxis incorrectos de la solicitud, falla del recurso, etc.;

5xx: El servidor no respondió, el servidor tiene un error interno o la carga es demasiado pesada.

5.3 Diferenciar las condiciones de la red

El cliente puede decidir cómo ejecutar la solicitud y qué tipo de datos solicitar de acuerdo con el estado de la conexión de red Independientemente de utilizar la interfaz HttpURLConnect mencionada anteriormente o una biblioteca de solicitud de red de terceros, se debe realizar un procesamiento diferente, al menos incluyendo diferentes tiempos de espera de solicitud y diferentes respuestas de datos.

5.3.1 Tipos de conexión

NetworkInfo se obtiene llamando a ConnectionManager.getActiveNetworkInfo(), y NetworkInfo.getType() obtiene el tipo de conexión de red específico, generalmente de la siguiente manera:

l TIPO_MÓVIL

n TelephonyManager.NETWORK_TYPE_LTE

n TelephonyManager.NETWORK_TYPE_GPRS

TIPO_WIFI

l TYPE_WIMAX es acceso global de interconexión por microondas, la tecnología 4G de Sprint en Estados Unidos . Es una tecnología de acceso inalámbrico de banda ancha emergente, que puede proporcionar una conexión orientada a Internet de alta velocidad, y la distancia de transmisión de datos puede alcanzar hasta 50 km.

l TIPO_ETHERNET Ethernet

TIPO_BLUETOOTH

Por ejemplo, en una aplicación móvil, cuando sea necesario descargar un archivo grande, debemos tratar de evitar ejecutarlo en el estado de la red móvil, o recordarle al usuario que se conecte a wifi; en una aplicación de TV, al menos juzgar si hay es una red a través de NetworkInfo.isConnected() disponible.

Además, debemos escuchar el evento ConnectivityManager.CONNECTIVITY_ACTION a través de BroadcastReceiver y registrarnos en la capa de servicio, y recordar a los usuarios globalmente la información de red cerrada o deficiente.

5.3.2 Usar diferentes estrategias de solicitud según las diferentes redes

Como se muestra arriba, podemos obtener el estado de la red en tiempo real, por lo que debemos establecer diferentes estrategias de solicitud de acuerdo con el tipo de red actual. La estrategia específica la determina la aplicación de acuerdo con su propio negocio, al menos incluyendo el tiempo de espera de la solicitud y la gran cantidad de recursos. configuraciones de ajuste.

Un ejemplo del uso de ConnectivityManager y TelephonyManager para evaluar de manera integral la red es el siguiente:

/** 没有网络 */
public staticfinal int NETWORKTYPE_INVALID = 0;
/** wap网络 */
public staticfinal int NETWORKTYPE_WAP = 1;
/** 2G网络 */
public staticfinal int NETWORKTYPE_2G = 2;
/** 3G和3G以上网络,或统称为快速网络 */
public staticfinal int NETWORKTYPE_3G = 3;
/** wifi网络 */
public staticfinal int NETWORKTYPE_WIFI = 4;

/**
 * 获取网络状态,wifi,wap,2g,3g.
 *
 * @param context 上下文
 * @return int 网络状态 {@link #NETWORKTYPE_2G},{@link#NETWORKTYPE_3G},          *{@link#NETWORKTYPE_INVALID},{@link #NETWORKTYPE_WAP}*<p>{@link #NETWORKTYPE_WIFI}
 */
public staticint getNetWorkType(Context context) {
    int mNetWorkType = 0;
    ConnectivityManager manager =(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo =manager.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
        String type =networkInfo.getTypeName();
        if (type.equalsIgnoreCase("WIFI")) {
            mNetWorkType = NETWORKTYPE_WIFI;
        } else if (type.equalsIgnoreCase("MOBILE")) {
            String proxyHost =android.net.Proxy.getDefaultHost();
            mNetWorkType = TextUtils.isEmpty(proxyHost)
                    ? (isFastMobileNetwork(context)? NETWORKTYPE_3G : NETWORKTYPE_2G)
                    : NETWORKTYPE_WAP;
        }
    } else {
        mNetWorkType = NETWORKTYPE_INVALID;
    }
    return mNetWorkType;
}

private staticboolean isFastMobileNetwork(Context context) {
    TelephonyManager telephonyManager =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
    switch (telephonyManager.getNetworkType()) {
        case TelephonyManager.NETWORK_TYPE_1xRTT:
            return false; // ~ 50-100 kbps
        case TelephonyManager.NETWORK_TYPE_CDMA:
            return false; // ~ 14-64 kbps
        case TelephonyManager.NETWORK_TYPE_EDGE:
            return false; // ~ 50-100 kbps
        case TelephonyManager.NETWORK_TYPE_EVDO_0:
            return true; // ~ 400-1000 kbps
        case TelephonyManager.NETWORK_TYPE_EVDO_A:
            return true; // ~ 600-1400 kbps
        case TelephonyManager.NETWORK_TYPE_GPRS:
            return false; // ~ 100 kbps
        case TelephonyManager.NETWORK_TYPE_HSDPA:
            return true; // ~ 2-14 Mbps
        case TelephonyManager.NETWORK_TYPE_HSPA:
            return true; // ~ 700-1700 kbps
        case TelephonyManager.NETWORK_TYPE_HSUPA:
            return true; // ~ 1-23 Mbps
        case TelephonyManager.NETWORK_TYPE_UMTS:
            return true; // ~ 400-7000 kbps
        case TelephonyManager.NETWORK_TYPE_EHRPD:
            return true; // ~ 1-2 Mbps
        case TelephonyManager.NETWORK_TYPE_EVDO_B:
            return true; // ~ 5 Mbps
        case TelephonyManager.NETWORK_TYPE_HSPAP:
            return true; // ~ 10-20 Mbps
        case TelephonyManager.NETWORK_TYPE_IDEN:
            return false; // ~25 kbps
        case TelephonyManager.NETWORK_TYPE_LTE:
            return true; // ~ 10+ Mbps
        case TelephonyManager.NETWORK_TYPE_UNKNOWN:
            return false;
        default:
            return false;
    }
}

5.3.3 Cola de red débil

En el desarrollo de aplicaciones móviles, bajo la condición de red débil 2G, además de usar diferentes configuraciones de solicitud, también es necesario controlar la cantidad de solicitudes. En este momento, por un lado, evite que una gran cantidad de solicitudes se apropien de recursos entre sí, lo que resulta en una respuesta lenta; por otro lado, puede ir más allá e intentar iniciar solicitudes cuando la red mejore en un entorno con fluctuaciones de la red, a fin de obtener mejores resultados de solicitud.

Un ejemplo simple de administración de cola personalizada es el siguiente:

public class TransferQueue {
    private Queue<Request> queue;
    public void addRequest(Request request) {
        queue.add(request);
    }
    public void execute() {
        //Iteration over the queue for executions
    }
}

5.4 Reducir la resolución de DNS

La función principal del sistema de nombres de dominio DNS es buscar la dirección IP en la tabla de mapeo de red de acuerdo con la URL del nombre de dominio utilizada por la solicitud de la aplicación. En la práctica, este proceso puede llevar de decenas a cientos de ms, y también existe la riesgo de interceptación de resolución de DNS o incluso ataques de secuestro.

De acuerdo con las necesidades comerciales específicas, puede considerar el uso de una conexión directa de IP en lugar del acceso de nombre de dominio, para lograr solicitudes de red más rápidas. Sin embargo, el método de usar la conexión IP directa no es lo suficientemente flexible, cuando el servicio correspondiente cambia su dirección IP por alguna razón, el cliente no puede acceder a él.

Por lo tanto, la estrategia para reducir la resolución de DNS es:

Por un lado, para recursos URI relativamente estables, puede usar la conexión directa IP;

Por otro lado, para los recursos URI que pueden tener actualizaciones dinámicas potenciales, mientras se usa el método IP, cuando falla el acceso al método IP, agregue un método para cambiar al acceso de nombre de dominio;

Además, muchas aplicaciones utilizan la solución de resolución de nombres de dominio httpDNS para evitar el secuestro de dns locales, y también es una solución de acceso de alto rendimiento; aquellos que tienen las condiciones para construir sus propios servicios de resolución también pueden expandirse aún más para usar HTTPDNS en todos sus interfaces de puerta de enlace;

5.5 Solicitud de fusión

Para una solicitud http completa, primero se requiere la búsqueda de resolución de DNS y luego se establece una conexión a través de un protocolo de enlace; si es https, se requiere un protocolo de enlace TLS para establecer una conexión.

Las solicitudes frecuentes no solo consumirán en gran medida el rendimiento de la CPU y la energía del dispositivo, sino que también sobrecargarán los recursos de subprocesos y la memoria de la aplicación. Por lo tanto, las solicitudes de red deben reducirse tanto como sea posible, y las solicitudes que se pueden combinar deben combinarse tanto como sea posible. posible. Por supuesto, esto también implica el diseño de la arquitectura cliente/servidor, lo que requiere una negociación bidireccional al diseñar y optimizar la interfaz de red del servidor.

Para el desarrollo de clientes, podemos considerarlo desde dos aspectos:

Por un lado, se extraen más datos de forma adecuada en una solicitud, y los datos de varios módulos estrechamente relacionados se solicitan al servidor al mismo tiempo;

Por otro lado, si el servidor no tiene una interfaz lo suficientemente resistente, las solicitudes de datos de varios módulos estrechamente relacionados se ejecutan en la sucesión más estrecha posible.

5.6 Caché de datos sin conexión

Para datos pesados ​​como imágenes y archivos, el almacenamiento en caché de memoria y el almacenamiento en caché de disco se pueden utilizar para implementar una estrategia de almacenamiento en caché de varios niveles, que no solo ahorra tráfico de usuarios en aplicaciones móviles, sino que también evita demoras en la red y mejora la experiencia del usuario.

Ya existen marcos maduros de código abierto disponibles para implementaciones específicas de imágenes y caché de datos, y no se proporciona una tabla detallada aquí. Los desarrolladores pueden configurarlos de acuerdo con condiciones específicas.

Para el caso de usar HttpURLConnection, Android proporciona la interfaz de almacenamiento en caché relacionada con la clase HttpResponseCache de Api Level 14;

5.6.1 Ejemplo de caché HttpURLConnection

Primero configure el caché:

protected void onCreate(Bundle savedInstanceState) {
    try {
        File httpCacheDir = new File(getCacheDir(), "http");
        long httpCacheSize = 0;
        HttpResponseCache.install(httpCacheDir, httpCacheSize);
    } catch (IOException e) {
        Log.i(getClass().getName(), "HTTP response cache installationfailed:" + e);
    }
}

Vacíe el caché al salir:

protected void onStop() {
    
    HttpResponseCache cache =HttpResponseCache.getInstalled();
    if (cache != null) {
        cache.flush();
    }
}

Agregue la estrategia de caché que debe aplicarse la próxima vez que se obtengan datos:

conexión.addRequestProperty("Cache-Control",POLICY);

Los valores para la POLÍTICA incluyen:

l sin caché: use completamente la descarga de red;

l max-age=SECONDS: el cliente recibirá datos cuya edad sea menor que este valor

l max-stale=SEGUNDOS: el cliente recibirá datos cuyo vencimiento sea menor a este valor

l solo si está en caché: use solo el caché, si no hay un archivo de caché, se lanzará una excepción FileNotFoundException.

5.6.2 Ejemplo de caché Okhttp


public class RetrofitManager {
    private static OkHttpClient mOkHttpClient;
    private static final String TAG = "OkHttp";

    //短缓存1分钟
    public static final int CACHE_AGE_SHORT = 60;
    //长缓存有效期为1天
    public static final int CACHE_STALE_LONG = 60 * 60 * 24;
    //自己的base_url
    public static final String BASE_URL = "http://www.baidu.com";

    private ApiServer apiServer;
    private static RetrofitManager instance = new RetrofitManager();

    public static RetrofitManager getInstance() {
        return instance;
    }

    private RetrofitManager() {
        initOkHttpClient();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(mOkHttpClient)
               .addConverterFactory(GsonConverterFactory.create())
                .build();
        apiServer = retrofit.create(ApiServer.class);
    }

    private void initOkHttpClient() {
        //日志拦截,使用okhttputils的HttpLoggingInterceptor,设置请求级别
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.e(TAG, message);
            }
        });
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //缓存的cookieJar负责管理cookies
        com.zhy.http.okhttp.cookie.CookieJarImplcookieJar = new com.zhy.http.okhttp.cookie.CookieJarImpl(new PersistentCookieStore(App.getInstance()));
        //单例
        if (mOkHttpClient == null) {
            synchronized (OkHttpClient.class) {
                if (mOkHttpClient == null) {
                    // 指定缓存路径,缓存大小10Mb
                    Cache cache = new Cache(new File(App.getInstance().getCacheDir(), "HttpCache"),
                            1024 * 1024 * 10);
                    mOkHttpClient = new OkHttpClient.Builder()
                            .cache(cache)
                           .addInterceptor(interceptor)
                           .addNetworkInterceptor(mCacheControlInterceptor)
                           .cookieJar(cookieJar)
                           .retryOnConnectionFailure(true)
                            .connectTimeout(60, TimeUnit.SECONDS)
                            .readTimeout(60, TimeUnit.SECONDS)
                           .writeTimeout(60, TimeUnit.SECONDS)
                            .build();
                }
            }
        }
    }

    public static ApiServer build() {
        return instance.apiServer;
    }

    // 响应头拦截器,用来配置缓存策略
    private Interceptor mCacheControlInterceptor = new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            Request request =chain.request();
            if (!NetUtils.isConnected(App.getInstance())){
                //没网时只使用缓存
                //自定义请求头,可以在响应头对请求头的header进行拦截,配置不同的缓存策略
                request = request.newBuilder()
                        .header("head-request", request.toString())
                       .cacheControl(CacheControl.FORCE_CACHE)
                        .build();
            }
            Response response =chain.proceed(request);
            if (NetUtils.isConnected(App.getInstance())){
                //有网的时候读接口上的@Headers里的配置,你可以在这里进行统一的设置
                Log.e("Interceptor", "response: " + response.toString());
                //添加头信息,配置Cache-Control
               //removeHeader("Pragma") 使缓存生效
                return response.newBuilder()
                        .header("Cache-Control", "public, max-age=" + CACHE_AGE_SHORT)
                        .removeHeader("Pragma")
                        .build();
            } else {
                Log.e("Interceptor", "net not connect");
                return response.newBuilder()
                        .header("Cache-Control", "public,only-if-cached,max-stale=" + CACHE_STALE_LONG)
                        .removeHeader("Pragma")
                        .build();
            }
        }
    };

}

5.7 Optimizar el mecanismo de reconexión

Cuando falla una solicitud de red, especialmente cuando se agota el tiempo de espera, generalmente iniciamos el mecanismo de reconexión y realizamos varios reintentos. El problema que es fácil que ocurra cuando se vuelve a intentar es el sondeo continuo, y es simple usar la suspensión en el subproceso para despertar.

Por motivos de rendimiento, no se deben realizar demasiados sondeos en la aplicación, si es necesario se deben optimizar al menos los dos puntos siguientes:

Por un lado, el sondeo no puede realizarse a intervalos repetidos simples, el tiempo del ciclo de sondeo debe diseñarse de manera que se expanda gradualmente, como la multiplicación por pasos, y al mismo tiempo controlar el número de reintentos. Como sigue:

public class Backoff {
    private static final int BASE_DURATION = 1000;
    private static final int[] BACK_OFF = new int[]{1, 2, 4, 8,
            16, 32, 64};
    public static InputStream execute(String urlString) {
        for (int attempt = 0; attempt < BACK_OFF.length; attempt++) {
            try {
                URL url = new URL(urlString);
                HttpURLConnection connection =
                       (HttpURLConnection) url.openConnection();
                connection.connect();
                return connection.getInputStream();
            } catch (SocketTimeoutException |
                    SSLHandshakeExceptione) {
                try {
                    Thread.sleep(BACK_OFF[attempt] *
                            BASE_DURATION);
                } catch (InterruptedException ex) {
                    throw new RuntimeException(ex);
                }
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }
}

Por otro lado, como en el ejemplo anterior, al reintentar y esperar, use Thread.Sleep() para simplemente esperar en el hilo, consumir recursos del hilo y ocupar la CPU al mismo tiempo.Si el intervalo de sondeo es grande, el el consumo es más serio, lo cual es mejor La mejor manera es usar el Administrador de alarmas del sistema para lograr la sincronización, lo que puede garantizar que la CPU también pueda estar inactiva cuando el sistema está inactivo, y se activará cuando sea necesario iniciar la siguiente solicitud de red .

5.8 Datos comprimidos

En términos de ahorrar tráfico de datos móviles y mejorar la velocidad de respuesta de la aplicación, necesitamos reducir la transmisión de datos de la red. Para el cliente, consideramos principalmente la compresión de datos desde dos aspectos:

Por un lado, los datos enviados al servidor se pueden comprimir, normalmente en formato GZIP;

Por otro lado, adoptar un mejor formato de transmisión de datos requiere usar json o un mejor formato para transportar datos, y considerar el formato de página web en lugar de JPEG/PNG para transportar imágenes.

5.9 Análisis de serialización de datos

Las solicitudes de red suelen ir acompañadas de serialización de datos y análisis de datos de respuesta.Actualmente, el formato json es más popular. Aunque Android proporciona una API de análisis de Json nativa, la velocidad es lenta y la interfaz no es lo suficientemente concisa. Por lo tanto, generalmente se utilizan otras excelentes bibliotecas de análisis json.

5.9.1 Elección de la biblioteca Json

Teniendo en cuenta el rendimiento y la capacidad de actualización continua, recomendamos el oficial Gson y Ali Fastjson. Entre ellos, Fastjson es una biblioteca para la plataforma Java. Si se usa exclusivamente en la plataforma Android, se puede usar su versión personalizada de Android.

Además, LoganSquare es una librería que ha surgido en los últimos dos años, ha sido especialmente optimizada para Android y, según los datos de prueba de su autor en el modelo MotoX, es mejor que Gson, especialmente al analizar archivos JSON de gran tamaño . El rendimiento es mucho mejor que GSON .

Observación:

1 Los estudiantes que todavía usan Eclipse no pueden usar LoganSquare , que solo admite la configuración de Gradle , dirección de GITHUB : https://github.com/bluelinelabs/LoganSquare ;

2 LoganSquare usa anotaciones en la clase Entity y proporciona devoluciones de llamada, lo que aumenta una cierta cantidad de trabajo y mejora la flexibilidad.

5.9.2 Optimización de la estructura del objeto Json

El objetivo principal de la optimización es reducir la redundancia innecesaria de la estructura de datos. archivo Json como se muestra a continuación :

[
        {
        "level": 23,
        "name": "Marshmallow",
        "version": "6.0"
        }, {
        "level": 22,
        "name": "Lollipop",
        "version": "5.1"
        }, {
        "level": 21,
        "name": "Lollipop",
        "version": "5.0"
        }, {
        "level": 19,
        "name": "KitKat",
        "version": "4.4"
        }
]

Adopta la estructura de varias matrices de objetos Json , que en realidad se pueden optimizar a la estructura de las matrices Json ordinarias, como se muestra a continuación:

{
    "level": [23, 22, 21, 19],
    "name": ["Marshmallow", "Lollipop", "Lollipop", "Kitkat"],
    "version": ["6.0", "5.1", "5.0", "4.4"]
}

El formato Json simplificado pronto reducirá el tamaño de los datos, y todo es un objeto Json , y la velocidad de análisis también mejorará considerablemente.

PD: Si eres un estudiante que ha usado Python y JS, puedes sentir intuitivamente el encanto de la función zip aquí, y los estudiantes de Java expresan un poco de emoción.

5.9.3 Evitar la serialización del almacenamiento local

La serialización permite que los datos se transmitan en diferentes entornos de forma ligera, lo que es muy beneficioso para la comunicación en red. Sin embargo, se debe evitar la serialización en el almacenamiento local porque la serialización y la deserialización requieren mucho tiempo.

Un ejemplo típico es almacenar en caché el archivo JSON localmente, de modo que cada vez que se use, debe deserializarse; además, si es necesario modificar el contenido, generalmente debe serializarse como un todo antes de sobrescribirlo. Esto es más costoso que usar bases de datos sqlite y otros métodos.

 

Supongo que te gusta

Origin blog.csdn.net/qq_16206535/article/details/79935148
Recomendado
Clasificación