Prefacio: Hace algún tiempo, al desarrollar aplicaciones, a menudo no podía obtener los datos obtenidos de la red debido al entorno del dispositivo del usuario, por lo que los resultados que se muestran en la aplicación siempre son una caja en blanco. Esta situación es extremadamente mala para la experiencia del usuario. Por lo tanto, pensé mucho y decidí comenzar con OKHTTP (porque el marco de solicitud de red que utilicé en el proyecto es OKHTTP), y escribí dicho interceptor de caché de datos de red.
Ok, entonces decidimos comenzar a escribir. Déjame hablar primero sobre mis ideas:
Ideas
Dado que estamos escribiendo sobre interceptores de caché de datos de red, que utilizan principalmente la poderosa función de interceptor de OKHTTP, ¿qué datos deberíamos almacenar en caché, o en qué circunstancias debemos habilitar el mecanismo de caché de datos?
Primero: las solicitudes de publicación son compatibles, porque el funcionario ha proporcionado un interceptor de caché, pero una desventaja es que solo pueden almacenar en caché los datos solicitados por GET, pero no admiten la publicación.
Segundo: cuando la red es normal, es obtener datos de la red. Si la red es anormal, como TimeOutException UnknowhostException y otros problemas, entonces necesitamos almacenar en caché y recuperar datos.
Tercero: si los datos obtenidos del caché están vacíos, entonces aún debemos dejar que esta solicitud pase por el proceso normal restante.
Cuarto: la persona que llama debe tener un control completo sobre el mecanismo de almacenamiento en caché y puede decidir selectivamente si almacenan en caché los datos de acuerdo con sus necesidades comerciales.
Quinto: debe ser simple de usar, que es el punto más importante.
Ok, hemos enumerado cinco puntos anteriores, que son nuestras ideas generales. Hablemos de la parte del código ahora:
Artículo de código
Marco de almacenamiento en caché: el marco de caché que uso aquí es disklrucache https://github.com/jakewharton/disklrucache Este marco de caché puede almacenarse localmente y ha sido aprobado por Google. Esta es también la razón principal para elegir este marco. También encapsulé una clase de caché al marco de caché:
importardroid.content.context; import android.content.pm.packageInfo; import android.content.pm.packageManager; import com.xiaolei.okhttpcacheinterceptor.log.log; import java.io.byteartutStream; import java.io.file; import java.io.fileinsam; java.io.ioException; import java.io.outputstream; import java.io.unsupportedencodingException; import java.security.messagedigest; import java.security.nosuchalgorithmexexception;/*** creado por Xiaolei en 2017/5/17. */public class Cachemanager {public static final String tag = "Cachemanager"; // tamaño de caché máximo 10MB estático privado final largo disk_cache_size = 1024 * 10; Private static final int disk_cache_index = 0; cadena final estática privada cache_dir = "respuestas"; Disklrucache privado mDisklRucache; Volátil Volátil Private Cachemanager McAchemanager; public static Cachemanager getInstance (contexto context) {if (mcachemanager == null) {sincronizado (Cachemanager.class) {if (mcachemanager == null) {mcachemanager = new Cachemanager (context); }}} return McAchemanager; } private Cachemanager (contexto de contexto) {File DiskCachedir = getDiskCachedir (context, cache_dir); if (! diskcachedir.exists ()) {boolean b = diskcachedir.mkdir (); Log.d (etiqueta, "! DiskCachedir.exists () --- diskCachedir.mkdir () =" + b); } if (diskCachedir.getUsableSpace ()> disk_cache_size) {try {mDisklrucache = disklrucache.open (diskCachedir, getAppVersion (context), 1/*cuántos archivos corresponden a una clave*/, disk_cache_size); Log.d (etiqueta, "mdisklrucache creado"); } catch (ioException e) {E.PrintStackTrace (); }}} / *** Establezca sincrónicamente Cache* / public void PutCache (clave de cadena, valor de cadena) {if (mDisklRucache == null) return; OutputStream OS = NULL; intente {disklrucache.Editor editor = mDisklRucache.Edit (ciRYPTMD5 (KEY)); OS = editor.newoutputStream (disk_cache_index); OS.Write (value.getBytes ()); OS.Flush (); editor.commit (); mdisklrucache.flush (); } catch (ioException e) {E.PrintStackTrace (); } finalmente {if (os! = null) {try {os.close (); } catch (ioException e) {E.PrintStackTrace (); }}}} / *** Establecer asíncronamente cache* / public void setCache (tecla de cadena final, valor de cadena final) {new Thread () {@Override public void run () {PutCache (clave, valor); } }.comenzar(); } /*** getCache getCache (tecla de cadena) {if (mDisklRucache == null) {return null; } FileInputStream fis = null; BytearRayOutputStream bos = null; Pruebe {disklrucache.snapshot snapshot = mDisklrucache.get (encryptmd5 (key)); if (snapshot! = null) {fis = (fileInputStream) snapshot.getInputStream (disk_cache_index); bos = new byteArRaReOutputStream (); byte [] buf = nuevo byte [1024]; int len; while ((len = fis.read (buf))! = -1) {bos.write (buf, 0, len); } byte [] data = bos.tobytearray (); devolver nueva cadena (datos); }} catch (ioException e) {E.PrintStackTrace (); } finalmente {if (fis! = null) {try {fis.close (); } catch (ioException e) {E.PrintStackTrace (); }} if (bos! = null) {try {bos.close (); } catch (ioException e) {E.PrintStackTrace (); }}} return null; } / *** Asynchrony GetCache* / public void getCache (tecla de cadena final, devolución de llamada de Cachecallback final) {new Thread () {@Override public void run () {String cache = getCache (key); callback.ongetCache (caché); } }.comenzar(); } / *** eliminar caché* / public boolean removeCache (tecla de cadena) {if (mDisklrucache! = Null) {try {return mDisklrucache.remove (cifryptmd5 (key)); } catch (ioException e) {E.PrintStackTrace (); }} return false; } / *** Get Cache Directory* / Private GetDiskCachedir (contexto context, string uniqueName) {String Cachepath = context.getCachedir (). GetPath (); devolver nuevo archivo (Cachepath + File.separator + uniquename); } / *** MD5 Codificación de la cadena* / public static String CiCryptmd5 (String String) {try {byte [] hash = messageDigest.getInstance ("md5"). Digest (string.getBytes ("utf-8"); StringBuilder hex = new StringBuilder (Hash.Length * 2); for (byte b: hash) {if ((b & 0xff) <0x10) {hex.append ("0"); } hex.append (integer.tohexstring (b & 0xff)); } return hex.ToString (); } Catch (nosuchalgorithMexception | UnsupportedEncodingException e) {E.PrintStackTrace (); } cadena de retorno; } / *** Obtener el número de versión de la aplicación* / private int getAppVersion (contexto context) {PackageManager pm = context.getpackageManager (); Pruebe {paqueteInfo pi = pm.getpackageInfo (context.getpackageName (), 0); return pi == null? 0: pi.versionCode; } Catch (PackageManager.NamenotFoundException e) {E.PrintStackTrace (); } return 0; }} Cacheinterceptor Interceptor: utiliza el mecanismo de interceptor interceptor de OKHTTP para juzgar de manera inteligente los escenarios de caché y las condiciones de la red, y manejar diferentes escenarios.
importardroid.content.context; import com.xiaolei.okhttpcacheinterceptor.catch.cachemanager; import com.xiaolei.okhttpcacheinterceptor.log.log; import java.io.ioException; import okhttp3.formbody; import okhttp3.interceptor; import okhttp3 okhttp3.request; import okhttp3.Response; import okhttp3.ResponseBody;/*** Cache Class of String* creada por Xiaolei el 2017/12/9. */public class Cacheinterceptor implementa Interceptor {contexto de contexto privado; public void setContext (contexto context) {this.context = context; } public cacheinterceptor (contexto context) {this.context = context; } @Override Public Response Intercept (cadena de cadena) arroja IOException {request Sold = Chain.Request (); Cadena cachehead = request.header ("caché"); Cadena cache_control = request.header ("Cache-Control"); if ("true" .equals (cachehead) || // significa caché (cache_control! = null &&! cache_control.isempty ())) // también es compatible con los encabezados de caché para los protocolos del lado web {long oldnow = system.currentTimemillis (); Cadena url = request.url (). Url (). ToString (); String ResponseStR = NULL; String reqBodyStr = getPostParams (solicitud); intente {Respuesta respuesta = chain.proced (solicitud); if (respuesta.issuccessful ()) // El procesamiento de caché se realiza solo después de que la solicitud de red se devuelve correctamente. De lo contrario, 404 se almacena en el caché. ¿No es una broma? {ResponseBody ResponseBody = Response.Body (); if (ResponseBody! = NULL) {ResponseRest = ResponseBody.String (); if (Respuesta == NULL) {Responsestr = ""; } Cachemanager.getInstance (context) .SetCache (Cachemanager.Encryptmd5 (URL + ReqBodyStr), Response); // Almacene el caché, use el enlace + parámetro a md5 codificación como almacenamiento clave de almacenamiento log.i ("httpretrofit", "-> Push cache:" + url + ": exitoso"); } return getOnlinerSponse (respuesta, respuestas); } else {return chain.proced (solicitud); }} capt (excepción e) {respuesta respuesta = getCacheresponse (solicitud, oldnow); // Se produjo una excepción, comencé a almacenar en caché aquí, pero puede no estar en caché, por lo que necesito lanzarlo a la siguiente ronda de procesamiento durante mucho tiempo if (Respuesta == NULL) {return Chain.proCED (solicitud); // dejar caer a la siguiente ronda de procesamiento} más {Respuesta de retorno; }}} else {return chain.proced (solicitud); }} Respuesta privada getCacheresponse (solicitud de solicitud, Long Oldnow) {log.i ("httpretrofit", "-> intente obtener caché ---------"); Cadena url = request.url (). Url (). ToString (); String Params = getPostParams (solicitud); String Cachestr = Cachemanager.getInstance (context) .getCache (Cachemanager.Encryptmd5 (url + params)); // Get Cache, use los parámetros de enlace + en la codificación de md5 a la clave para obtener if (cachestr == null) {log.i ("httpretrofit", "<-get cache fails --------------"); ");"); "); regresar nulo; } Respuesta Respuesta = nueva respuesta.Builder () .code (200) .Body (ResponseBody.Create (NULL, CACHESTR)) .Request (solicitud) .message ("Ok") .protocol (Protocol.http_1_0) .Build (); Long Usetime = System.CurrentTimemillis () - Oldnow; Log.i ("httpretrofit", "<- get cache:" + respuesta.code () + "" + respuesta.message () + "" + url + "(" + useTime + "ms)"); Log.i ("httpretrofit", cachestr + ""); Respuesta de retorno; } Respuesta privada getOnlinerSponse (respuesta de respuesta, cuerpo de cadena) {ResponseBody ResponseBody = Response.Body (); return New Response.Builder () .Code (Response.Code ()) .Body (ResponseBody.Create (ResponseBody == NULL? NULL: ResponseBody.ContentType (), Body)) .Request (Response.Request ()). PeSesge (Response.Message ()). } /*** Obtenga en modo Post. Parámetros enviados al servidor * * @param solicitud * @return */ private string getPostParams (solicitud de solicitud) {String reqBodyStr = ""; Método de cadena = request.method (); if ("post" .equals (método)) // Si está post, analice cada parámetro tanto como sea posible {stringBuilder sb = new StringBuilder (); if (request.body () instanceOf formBody) {formBody Body = (formBody) request.body (); if (body! = null) {for (int i = 0; i <body.size (); i ++) {sb.append (body.encodedName (i)). append ("="). append (body.EncodedValue (i)). append (","); } sb.delete (sb.length () - 1, sb.length ()); } reqBodyStr = sb.ToString (); sb.delete (0, sb.length ()); }} return reqBodyStr; }}Lo anterior es la idea principal y el código de implementación principal. Hablemos del método de uso ahora
Cómo usar:
Gradle usa:
compilar 'com.xiaolei: okhttpcacheinterceptor: 1.0.0'
Dado que acaba de enviarlo a JCenter, es posible que no pueda retirarlo (aún no se ha revisado). Un lector ansioso puede agregar mi enlace Maven a los repositorios en su proyecto: Build.gradle:
AllProjects {repositorios {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}Creamos un nuevo proyecto, y la captura de pantalla del proyecto es la siguiente:
Captura de pantalla del proyecto
La demostración es muy simple, una página principal, un frijol, una modernización, una interfaz de solicitud de red
Tenga en cuenta que debido a que es una red, caché y relacionado, no hay duda de que necesitamos agregar permisos de solicitud de red y permisos de lectura y escritura de archivos en el manifiesto:
<use-permission android: name = "android.permission.internet" /> <use-permission android: name = "android.permission.read_external_storage" /> <use-permission android: name = "android.permission.write_external_storage" />>
Al usarlo, solo necesita agregar un interceptor a su OkhttpClient:
Client = new OkhttpClient.Builder () .addinterceptor (new Cacheinterceptor (context)) // Agregar interceptor de caché, Agregar soporte de caché.rcryonConnectionFailure (true) // falló el reconnect.connecttimeout (30, timeUnit.seconds) // La unidad de tiempo de espera de la solicitud de red es segundo.build ();
Si desea almacenar en caché los datos de qué interfaz, durante mucho tiempo, agregue un encabezado de solicitud para su interfaz de red. La clase CacheHeaders.Java contiene todas las situaciones. En general, solo se necesita caché. Normal.
Public Interface Net {@headers (cacheheaders.normal) // Aquí está la clave @FormurlEncoded @Post ("Geocoding") Llame pública <StaBean> getIndex (@field ("A") String A);}Código de negocio:
Net net = retrofitBase.getRecrofit (). Create (net.class); Llamar <Databean> call = net.getIndex ("suzhou city"); call.enqueue(new Callback<DataBean>() { @Override public void onResponse(Call<DataBean> call, Response<DataBean> response) { DataBean data = response.body(); Date date = new Date(); textview.setText(date.getMinutes() + " " + date.getSeconds() + ":/n" + data + ""); } @Override public void OnFailure (llamar <DataBean> Llame, THOLEABLE t) {TextView.setText ("¡Solicitar fallido!");Si nuestra solicitud de red es exitosa, emitiremos texto en la interfaz y agregaremos la hora actual. Si la red falla, la salida de una solicitud falla.
Probablemente este es el código, el código detallado se publicará al final del artículo.
Mira el efecto: demo
Aquí demostramos que desde la red normal hasta la red anormal, y luego volviendo a situaciones normales.
Final
El capítulo anterior es todo el proceso desde ideas, código y luego hasta representaciones. Aquí está la dirección de la demostración. Si le gusta, puede hacer clic en Inicio.
Dirección de demostración: https://github.com/xiaolei123/okhttpcacheinterceptor
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.