Préface: il y a quelque temps, lors du développement d'applications, je ne pouvais souvent pas obtenir les données obtenues du réseau en raison de l'environnement de l'appareil de l'utilisateur, de sorte que les résultats affichés sur l'application sont toujours une boîte vide. Cette situation est extrêmement mauvaise pour l'expérience utilisateur. Par conséquent, j'ai pensé dur et j'ai décidé de commencer par OKHTTP (car le cadre de demande de réseau que j'ai utilisé dans le projet est OKHTTP), et j'ai écrit un tel intercepteur de cache de données réseau.
Ok, alors nous avons décidé de commencer à écrire. Permettez-moi d'abord de parler de mes idées:
Idées
Puisque nous écrivons sur les intercepteurs de cache de données réseau, qui utilisent principalement la puissante fonction d'intercepteur d'OKHTTP, quelles données devons-nous mettre en cache, ou dans quelles circonstances devrions-nous activer le mécanisme de cache de données?
Premièrement: les demandes de publication sont prises en charge, car le responsable a fourni un intercepteur de cache, mais un inconvénient est qu'ils ne peuvent mettre en cache que les données demandées par GET, mais ils ne prennent pas en charge la publication.
Deuxièmement: lorsque le réseau est normal, il faut récupérer les données du réseau. Si le réseau est anormal, tel que TimeoutException inconnu et d'autres problèmes, nous devons cacher et récupérer des données.
Troisièmement: si les données récupérées à partir du cache sont vides, nous devons toujours laisser cette demande passer par le processus normal restant.
Quatrièmement: l'appelant doit avoir un contrôle complet sur le mécanisme de mise en cache et peut décider sélectivement de mettre en cache les données en fonction de ses besoins commerciaux.
Cinquièmement: il doit être simple à utiliser, ce qui est le point le plus important.
OK, nous avons énuméré cinq points ci-dessus, qui sont nos idées générales. Parlons maintenant de la partie du code:
Article de code
Framework de mise en cache: Le framework de cache que j'utilise ici est DisklRucache https://github.com/jakewharton/disklrucache Ce cadre de cache peut être stocké localement et a été approuvé par Google. C'est également la principale raison du choix de ce cadre. J'imagine également une classe CacheManager dans le cadre de cache:
import android.content.context; import android.content.pm.packageinfo; import android.content.pm.packagemanager; import com.xiaolei.okhttpcacheinterceptor.log.log; java.io.ioException; Importer java.io.outputStream; import java.io.unsupportEncodingException; importer java.security.messagedigest; importer java.security.nosuchalgorithmexception; / ** * créé par Xiaolei le 2017/5/17. * / classe publique CacheManager {public static final string tag = "cacheManager"; // Taille de cache max 10 Mo private statique finale longue disque_cache_size = 1024 * 10; private static final int disk_cache_index = 0; chaîne finale statique privée cache_dir = "réponses"; DisklRucache privée mdisklrucache; Cachemanager statique volatile privé Mcachemanager; public static cacheManager getInstance (context context) {if (mcacheManager == null) {synchronisé (cacheManager.class) {if (mcacheManager == null) {mcacheManager = new cacheManager (context); }}} return mcacheManager; } private cacheManager (contexte de contexte) {fichier diskcachedir = getDiskCachedir (contexte, cache_dir); if (! diskcachedir.exists ()) {boolean b = diskcachedir.mkdir (); Log.d (tag, "! Diskcachedir.exists () --- diskcachedir.mkdir () =" + b); } if (diskcachedir.getUsablespace ()> disk_cache_size) {try {mDisklRucache = disklrucache.open (diskcachedir, getAppVersion (context), 1 / * combien de fichiers correspondent à une clé * /, disk_cache_size); Log.D (Tag, "MdisklRucache Created"); } catch (ioException e) {e.printStackTrace (); }}} / ** * Définir synchrone Cache * / public void putCache (clé de chaîne, valeur de chaîne) {if (mdisklrucache == null) return; OutputStream os = null; essayez {disklrucache.editor editor = mDisklRucache.edit (encryptmd5 (key)); OS = editor.newOutputStream (disk_cache_index); os.write (value.getBytes ()); os.flush (); editor.Commit (); mdisklrucache.flush (); } catch (ioException e) {e.printStackTrace (); } enfin {if (os! = null) {try {os.close (); } catch (ioException e) {e.printStackTrace (); }}}} / ** * Définir asynchrone Cache * / public void setCache (clé de chaîne finale, valeur finale de chaîne) {new Thread () {@Override public void run () {putCache (key, value); } }.commencer(); } / ** * Synchrone GetCache getCache (string key) {if (mDisklRucache == null) {return null; } FileInputStream fis = null; BytearrayoutputStream bos = null; try {disklrucache.snapshot snapshot = mDisklRucache.get (Encryptmd5 (key)); if (snapshot! = null) {fis = (fileInputStream) snapshot.getInputStream (disk_cache_index); bos = new bytearrayoutputStream (); octet [] buf = nouveau octet [1024]; int len; while ((len = fis.read (buf))! = -1) {bos.write (buf, 0, len); } octet [] data = bos.toByTearray (); renvoyer une nouvelle chaîne (données); }} catch (ioException e) {e.printStackTrace (); } enfin {if (fis! = null) {try {fis.close (); } catch (ioException e) {e.printStackTrace (); }} if (bos! = null) {try {bos.close (); } catch (ioException e) {e.printStackTrace (); }}} return null; } / ** * Asynchrone GetCache * / public void getCache (touche de chaîne finale, Final CacheCallback callback) {new Thread () {@Override public void run () {String cache = getCache (key); callback.ongetcache (cache); } }.commencer(); } / ** * Supprime le cache * / public boolean removeCache (string key) {if (mDisklRucache! = Null) {try {return mdisklrucache.remove (encryptmd5 (key)); } catch (ioException e) {e.printStackTrace (); }} return false; } / ** * Get Cache Directory * / Private File GetDiskCachedir (Context Context, String UniqueName) {String cachepath = context.getCachedir (). GetPath (); return nouveau fichier (cachepath + file.separator + uniqueName); } / ** * MD5 Encodage de la chaîne * / public static String Encryptmd5 (String String) {try {byte [] hash = MessagediGest.getInSitance ("md5"). Digest (string.getBytes ("utf-8")); StringBuilder hex = new StringBuilder (hash.length * 2); pour (octet b: hash) {if ((b & 0xff) <0x10) {hex.append ("0"); } hex.append (Integer.tohexString (b & 0xff)); } return hex.toString (); } catch (NosuchalgorithMexception | UnsupportEncodingException e) {e.printStackTrace (); } return string; } / ** * Obtenez le numéro de version de l'application * / private int getAppversion (context context) {packageManager pm = context.getPackageManager (); essayez {packageInfo pi = pm.getPackageInfo (context.getPackageName (), 0); Retour pi == null? 0: PI.VersionCode; } catch (packageManager.NaMenotFoundException e) {e.printStackTrace (); } return 0; }} CacheInterceptor Interceptor: utilise le mécanisme intercepteur intercepteur d'OKHTTP pour juger intelligemment les scénarios de cache et les conditions de réseau et gérer différents scénarios.
import android.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.potocol; importation; okhttp3.request; import okhttp3.Response; import okhttp3.responsebody; / ** * classe de cache de chaîne * créée par xiaolei le 2017/12/9. * / classe publique CacheInterceptor implémente Interceptor {contexte de contexte privé; public void setContext (contexte contextuel) {this.context = context; } public cacheInterceptor (context context) {this.context = context; } @Override Public Response Intercept (chaîne de chaîne) lève ioException {request request = chain.request (); String cachehead = request.header ("cache"); String cache_control = request.Header ("Cache-Control"); if ("true" .equals (cachehead) || // Cela signifie cache (cache_control! = null &&! cache_control.isempty ())) // Il prend également en charge les en-têtes de cache pour les protocoles de côté Web {long oldNow = System.currenttimemillis (); String url = request.url (). Url (). ToString (); String Responsestr = null; String reqbodyStr = getPostParams (request); try {réponse réponse = chaîne.proceed (request); if (Response.SUCCESSFUL ()) // Le traitement du cache n'est effectué qu'après que la demande de réseau renvoie avec succès. Sinon, 404 est stocké dans le cache. N'est-ce pas une blague? {ResponseBody ResponseBody = Response.Body (); if (réponsebody! = null) {Responsestr = ResponseBody.String (); if (nAftr == null) {Responsestr = ""; } CacheManager.getInstance (context) .setCache (cacheManager.encryptmd5 (URL + reqbodystr), Responsestr); // Stockage le cache, utilisez le paramètre Link + à MD5 Encoding comme Log.i de stockage de clé ("Httpretrofit", "-> push cache:" + url + ": Success"); } return getOnlineResponse (Response, Responsestr); } else {return chain.proceed (request); }} catch (exception e) {réponse réponse = getCacheResponse (request, oldNow); // Une exception s'est produite, j'ai commencé à me mettre en cache ici, mais elle peut ne pas être mise en cache, donc je dois le lancer à la prochaine série de traitement pendant longtemps si (réponse == null) {return chain.proceed (request); // passer à la prochaine série de traitement} else {return réponse; }}} else {return chain.proceed (request); }} Réponse privée getCacheResponse (demande de demande, long oldNow) {log.i ("httpretrofit", "-> Essayez d'obtenir un cache ---------"); String url = request.url (). Url (). ToString (); String params = getPostParams (demande); String cachestr = cacheManager.getInstance (context) .getCache (cacheManager.Encryptmd5 (URL + params)); // Get Cache, utilisez le lien + paramètres à MD5 Encoding to Key pour obtenir if (cachestr == null) {log.i ("httpretrofit", "<- Get Cache Faiche --------"); retourner null; } Response Response = new Response.Builder () .code (200) .Body (réponse. Long UseeTime = System.Currenttimemillis () - OldNow; Log.i ("httpretrofit", "<- get cache:" + réponse.code () + "" + réponse.message () + "" + url + "(" + usetime + "ms)"); Log.i ("httpretrofit", cachestr + ""); réponse de retour; } Réponse privée getOnlineResponse (Response Response, String Body) {ResponseBody ResponseBody = Response.Body (); return new réponse.builder () .code (réponse.code ()) .body (réponse. } / ** * Obtenez en mode post. Paramètres envoyés au serveur * * @param request * @return * / private String getPostParams (request request) {String reqbodyStr = ""; String Method = request.Method (); if ("post" .equals (méthode)) // Si elle est post, analysez chaque paramètre autant que possible {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 ("="). annex (body.encodedValue (i)). APPEND (","); } sb.delete (sb.length () - 1, sb.length ()); } reqbodystr = sb.toString (); sb.delete (0, sb.length ()); }} return reqbodystr; }}Ce qui précède est l'idée principale et le code d'implémentation principal. Parlons de la méthode d'utilisation maintenant
Comment utiliser:
Gradle utilise:
compiler 'com.xiaolei: okhttpcacheinterceptor: 1.0.0'
Comme il vient d'être soumis à JCenter, il peut ne pas être en mesure de le réduire (il n'a pas encore été examiné). Un lecteur anxieux peut ajouter mon lien maven aux référentiels de votre projet: build.gradle:
AllProjects {Repositories {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}Nous créons un nouveau projet et la capture d'écran du projet est la suivante:
Capture d'écran du projet
La démo est très simple, une page principale, un haricot, une modernisation, une interface de demande réseau
Notez que parce qu'il s'agit d'un réseau, d'un cache et connexe, il ne fait aucun doute que nous devons ajouter des autorisations de demande de réseau et des autorisations de lecture et d'écriture dans le manifeste:
<use-permission Android: name = "android.permission.internet" /> <use-permission Android: name = "android.permission.read_external_storage" /> <us-permission Android: name = "Android.permission.write_external_storage" />
Lorsque vous l'utilisez, il vous suffit d'ajouter un intercepteur à votre okhttpclient:
client = new OKHTTPCLIENT.BUILDER () .AddInterceptor (new CacheInterceptor (context)) // Ajouter Cache Interceptor, ajouter Cache Support.RetryOnConnectionFailure (true) // Échec reconnect.connectTimeout (30, timeunit.seconds) // L'unité de délai de demande de réseau est seconde.build ();
Si vous souhaitez mettre en cache les données de quelle interface, alors pendant longtemps, ajoutez un en-tête de demande pour votre interface réseau. La classe Cacheheders.java contient toutes les situations. Généralement, seuls les caches.Normal est nécessaire.
Interface publique net {@heders (cacheheders.normal) // voici la clé @formurLenCcoded @post ("geocoding") Call public <dataBean> getIndex (@field ("a") chaîne a);}Code des affaires:
Net net = restrofitBase.getRetRofit (). Create (net.class); Call <DataBean> Call = net.getIndex ("Suzhou City"); Call.enqueue (nouveau rappel <DataBean> () {@Override public void onResponse (appel <dataBean> appel, réponse <ataBean> réponse) {dataean data = réponse.body (); date date = new Date (); textView.seTText (date.getMinutes () + "" + date.getseconds () + ": / n" + data + "); onFailure (appel <dataBean> Call, throwable t) {textView.seTText ("Demande a échoué!");}});Si notre demande de réseau est réussie, nous publierons du texte sur l'interface et ajouterons l'heure actuelle. Si le réseau échoue, la sortie d'une demande échoue.
C'est probablement le code, le code détaillé sera publié à la fin de l'article
Regardez l'effet: démo
Ici, nous démontrons que du réseau normal au réseau anormal, puis en revenant à des situations normales.
Fin
Le chapitre ci-dessus est l'ensemble du processus des idées, du code, puis des rendus. Voici l'adresse de la démo. Si vous l'aimez, vous pouvez cliquer sur Démarrer.
Adresse de démonstration: https://github.com/xiaolei123/okhttpcacheInterceptor
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.