Vorwort: Vor einiger Zeit konnte ich bei der Entwicklung von Apps aufgrund der Geräteumgebung des Benutzers oft nicht die Daten aus dem Netzwerk erhalten, sodass die in der App angezeigten Ergebnisse immer ein leeres Feld sind. Diese Situation ist äußerst schlecht für die Benutzererfahrung. Daher dachte ich mir schwer und beschloss, mit OKHTTP zu beginnen (weil das im Projekt verwendete Netzwerkanforderungsframework OKHTTP ist), und ich schrieb einen solchen Netzwerkdaten -Cache -Interceptor.
Ok, dann haben wir uns entschlossen, zu schreiben. Lassen Sie mich zuerst über meine Ideen sprechen:
Ideen
Da wir über Netzwerkdaten -Cache -Interceptors schreiben, die hauptsächlich die leistungsstarke Interceptor -Funktion von OKHTTP verwenden, welche Daten sollten wir unterbinden oder unter welchen Umständen wir den Datencache -Mechanismus aktivieren?
Erstens: Postanfragen werden unterstützt, da der Beamte einen Cache -Interceptor bereitgestellt hat, aber ein Nachteil ist, dass er nur die von GET angeforderten Daten zwischenspeichern kann, aber nicht den Beitrag unterstützen.
Zweitens: Wenn das Netzwerk normal ist, sollen Daten aus dem Netzwerk abgerufen werden. Wenn das Netzwerk abnormal ist, wie z. B. TimeoutException UnkundhostException und andere Probleme, müssen wir Daten zwischenspeichern und abrufen.
Drittens: Wenn die aus dem Cache abgerufenen Daten leer sind, müssen wir diese Anfrage immer noch im verbleibenden normalen Prozess durchlaufen lassen.
Viertens: Der Anrufer muss die vollständige Kontrolle über den Caching -Mechanismus haben und kann selektiv entscheiden, ob die Daten entsprechend seinen geschäftlichen Bedürfnissen zwischenstrichen werden sollen.
Fünfter: Es muss einfach zu bedienen sein, was der wichtigste Punkt ist.
Ok, wir haben oben fünf Punkte aufgeführt, die unsere allgemeinen Ideen sind. Lassen Sie uns jetzt über den Code -Teil sprechen:
Codeartikel
Caching -Framework: Das Cache -Framework, das ich hier verwende, ist disklrucache https://github.com/jakewharton/disklucache Dieses Cache -Framework kann lokal gespeichert und von Google genehmigt werden. Dies ist auch der Hauptgrund für die Auswahl dieses Frameworks. Ich bin auch eine CacheManager -Klasse in das Cache -Framework integriert:
android.content.context importieren; android.content.pm.packageInfo; import android.content. java.io.ioException; import Java.io.outputStream; Import Java.io.unsupportedenCodingException; Import Java.security.messagedigest; Import Java.security.nosuchalgorithmexception;/***, die von Xiaolei über 2017/5/17 erstellt wurden. */public class CacheManager {public static final String tag = "CacheManager"; // Max Cache Größe 10MB private statische endgültige lange Disk_cache_size = 1024 * 10; private statische endgültige INT DISK_Cache_Index = 0; private statische endgültige Zeichenfolge cache_dir = "Antworten"; private disklrucache mdisklucache; private volatile statische CacheManager McAchemanager; public static CacheManager getInstance (Kontextkontext) {if (mcAchemanager == null) {synchronized (cachemanager.class) {if (mcAchemanager == null) {McAchemanager = new CacheManager (Kontext); }}} return mcAchemanager; } private cacheManager (Kontextkontext) {Datei diskCachedir = getDiskCachedir (Kontext, Cache_Dir); if (! diskcachedir.exists ()) {boolean b = diskCachedir.mkdir (); Log.d (tag, "! DiskCachedir.exists () --- diskCachedir.mkdir () =" + b); } if (diskCachedir.getUsableSpace ()> disk_cache_size) {try {mdisklucache = disklrucache.open (diskCachedir, getAppversion (Kontext), 1/*wie viele Dateien einem Schlüssel entsprechen*/, disk_cache_size); Log.d (Tag, "Mdisklrucache erstellt"); } catch (ioException e) {e.printstacktrace (); }}} / *** Synchron Cache* / public void putcache (String -Taste, String -Wert) {if (mdisklucache == null) return; OutputStream os = null; try {disklrucache.editor editor = mdisklucache.edit (Encryptmd5 (Schlüssel)); os = editor.neWoutputStream (disk_cache_index); os.write (value.getBytes ()); os.flush (); editor.commit (); mdisklucache.flush (); } catch (ioException e) {e.printstacktrace (); } endlich {if (os! = null) {try {os.close (); } catch (ioException e) {e.printstacktrace (); }}}} / *** Asynchron setze Cache* / public void setCache (endgültiger String -Schlüssel, endgültiger String -Wert) {neuer Thread () {@Override public void run () {putcache (Schlüssel, Wert); } }.Start(); } /*** Synchron getCache getCache (String -Taste) {if (mdisklRucache == null) {return null; } FileInputStream fis = null; BytearrayoutputStream bos = null; try {disklrucache.snapshot snapshot = mdisklucache.get (Encryptmd5 (Schlüssel)); if (snapshot! BOS = neuer BytearrayoutputStream (); byte [] buf = neues byte [1024]; Int len; while ((len = fis.read (buf))! = -1) {bos.write (buf, 0, len); } byte [] data = bos.tobytearray (); Neue Zeichenfolge (Daten) zurückgeben; }} catch (ioException e) {e.printstacktrace (); } endlich {if (fis! = null) {try {fis.close (); } catch (ioException e) {e.printstacktrace (); }} if (bos! = null) {try {bos.close (); } catch (ioException e) {e.printstacktrace (); }}} return null; } / *** Asynchron GetCache* / public void getCache (endgültige Zeichenfolgeschlüssel, endgültiger CacheCallback -Rückruf) {neuer Thread () {@Override public void run () {String cache = getCache (Schlüssel); callback.ongetcache (cache); } }.Start(); } / *** Cache entfernen* / public boolean removecache (String -Taste) {if (mdisklucache! } catch (ioException e) {e.printstacktrace (); }} return false; } / *** Cache -Verzeichnis erhalten* / private Datei getDiskCachedir (Kontextkontext, String uniquename) {String cachepath = context.getCacheDir (). GetPath (); Neue Datei zurückgeben (Cachepath + Datei.Sesparator + Uniquename); } / *** MD5-Codierung der String* / public static String Encryptmd5 (String String) {try {byte [] Hash = MessagedIGest.getInstance ("Md5"). Digest (string.getBytes ("utf-8"); StringBuilder hex = new StringBuilder (Hash.Length * 2); für (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 (); } return String; } / *** App -Versionsnummer erhalten* / private int getAppversion (Kontextkontext) {PackAGemanager pm = context.getPackAGemanager (); try {packageInfo pi = pm.getPackageInfo (context.getPackagename (), 0); Pi == NULL zurückgeben? 0: pi.versionCode; } catch (packAGemanager.namenotfoundException e) {e.printstacktrace (); } return 0; }} CacheInterceptor Interceptor: Verwendet den Interceptor Interceptor -Mechanismus von OKHTTP, um Cache -Szenarien und Netzwerkbedingungen intelligent zu beurteilen und verschiedene Szenarien zu verarbeiten.
Importieren Sie android.content.context; import com.xiaolei.okhttpcacheInterceptor.catch.cachemanager; importieren com.xiaol.okhttpcacheInterceptor.log.Log; Okhttp3.Request; import OKHTTP3.Response; import OKHTTP3.ResponseBody;/*** Cache -Klasse von String* Erstellt von Xiaolei am 2017/12/9. */Public Class CacheInterceptor implementiert Interceptor {private Context Context; public void setContext (Kontextkontext) {this.context = context; } public CacheInterceptor (Kontextkontext) {this.context = context; } @Override public response intercept (Chain Chain) löscht IOException {Request Request = chain.request (); String cachehead = Request.Header ("Cache"); String cache_control = request.Header ("cache-control"); if ("true" .equals (cachehead) || // es bedeutet cache (cache_control! = null &&! String url = request.url (). Url (). ToString (); String responstr = null; String reqbodyStr = getPostParams (Anfrage); try {response response = chain.procece (request); if (response.issuccessful ()) // Cache -Verarbeitung wird erst nach der erfolgreichen Rückgabe der Netzwerkanforderung durchgeführt. Ansonsten wird 404 im Cache gespeichert. Ist es nicht ein Witz? {ResponseBody responseBody = response.body (); if (responseBody! if (antwortstr == null) {respotsESTR = ""; } Cachemanager.getInstance (Kontext) .SetCache (CACHEMANAGER.Encryptmd5 (url + reqbodystr), responstESTR); // Speichern Sie den Cache, verwenden Sie den Link + Parameter für MD5-Codierung als Schlüsselspeicherprotokoll ("httpraTrofit", "-> copp cache:" + url +: " + url +:"; } return getonlineresponse (Antwort, ResponstESTR); } else {return chain.procece (request); }} catch (Exception e) {response response = getCacheresponse (request, OldNow); // Eine Ausnahme trat auf, ich habe hier angefangen, zwischen dem zwischengestellten Bereich zu leiten, aber sie kann nicht zwischengespeichert werden. Daher muss ich sie für eine lange Zeit in die nächste Verarbeitungsrunde werfen, wenn (response == null) {return chain.procece (Anfrage); // fallen in die nächste Runde der Verarbeitung} {Rückgabe -Antwort; }}} else {return chain.procece (request); }} private Antwort getCacherePonse (Anfrageanforderung, lange altnow) {log.i ("httpretrofit", "-> Versuchen Sie, Cache zu erhalten ----------"); String url = request.url (). Url (). ToString (); String params = getPostParams (Anfrage); String cachestr = cachemanager.getInstance (Kontext) .getCache (cachemanager.encryptmd5 (url + params)); // Cache, verwenden Sie Link + Parameter zu md5 codieren, um zu tasten (cachestr == null) {lott.i ("httprafit", "<-Cache-Versagen -----"; null zurückkehren; } Response response = new response.builder () .code (200) .body (responseBody.create (null, cachestern)) .request (request) .message ("ok") .Protocol (protocol.http_1_0) .build (); long useetime = system.currentTimemillis () - Oldnow; Log.i ("httpretrofit", "<- cache erhalten:" + response.code () + "" + response.message () + "" + url + "(" + usetime + "ms)"); Log.i ("httpretrofit", cachestr + ""); Rücksendemantwort; } private Antwort getonlinerePonse (Antwortantwort, String Body) {responseBody responseBody = response.body (); return new response.builder () .code (response.code ()) .body (responseBody.create (responseBody == null? null: responseBody.contentType (), Körper) .Request (request.request ()) .message (reaktionsage () .Protocol (protokol () .build () .build () .build (). } /*** Steigen Sie im Post -Modus. Parameter an den Server gesendet * * @param request * @return */ private String getPostParams (Anfrageanforderung) {String reqBodyStr = ""; String -Methode = Request.Method (); if ("post" .equals (methode)) // Wenn es post ist, analysieren Sie jeden Parameter so weit wie möglich {StringBuilder sb = new StringBuilder (); if (request.body () instanceof Formbody) {Formbody Body = (FormBody) Request.body (); if (body! } sb.delete (sb.length () - 1, sb.length ()); } reqbodyStr = sb.toString (); sb.delete (0, sb.length ()); }} return reqbodystr; }}Das obige ist die Hauptidee und der Hauptimplementierungscode. Lassen Sie uns jetzt über die Nutzungsmethode sprechen
Wie man verwendet:
Gradle verwendet:
Kompilieren Sie 'com.xiaolei: okhttpcacheInterceptor: 1.0.0' '
Da es gerade bei JCenter eingereicht wurde, kann es möglicherweise nicht in die Lage versetzt werden (es wurde noch nicht überprüft). Ein ängstlicher Leser kann meinen Maven -Link zu den Repositorys in Ihrem Projekt hinzufügen: Build.gradle:
AllProjects {Repositories {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}Wir erstellen ein neues Projekt und der Projekt Screenshot lautet wie folgt:
Projekt Screenshot
Demo ist sehr einfach, eine Hauptseite, eine Bean, eine Nachrüstung, eine Netzwerkanforderungsschnittstelle
Beachten Sie, dass wir, da es sich um ein Netzwerk, einen Cache und ein verwandtes Zusammenhang handelt, keinen Zweifel daran, dass wir in dem Manifest Netzwerkanforderungsberechtigungen und Lesen und Schreiben von Dateien hinzufügen müssen:
<uswend-permission android: name = "android.permission.internet" /> <usad-permission android: name = "android.permission.read_external_storage" /> <usad-Permission Android: name = "android.permission.write_external_storage" />
Wenn Sie es verwenden, müssen Sie nur einen Interceptor zu Ihrem Okhttpclient hinzufügen:
client = new Okhttpclient.builder () .AdDInterceptor (neuer CacheInterceptor (Kontext)) // Cache -Interceptor hinzufügen, Cache Support.RetryonConnectionFailure (True) // fehlgeschlagene Wiederverbindung
Wenn Sie Daten von welcher Schnittstelle zwischenspeichern möchten, fügen Sie lange Zeit einen Anforderungsheader für Ihre Netzwerkschnittstelle hinzu. Die CachHeaders.java -Klasse enthält alle Situationen. Im Allgemeinen werden nur Cachheaders.normal benötigt.
public interface net {@headers (cacheheaders.normal) // Hier ist der Schlüssel @Formurlencoded @post ("Geocoding") public Call <Datenabesante> getIndex (@field ("a") String a);};};};Geschäftsordnung:
Net net = retrofitbase.getRetRofit (). Create (net.class); Call <Datenabesan> 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 (Call <Datenabesan> Anruf, Throwable T) {textView.setText ("Anfrage fehlgeschlagen!");Wenn unsere Netzwerkanforderung erfolgreich ist, geben wir Text auf der Schnittstelle aus und fügen die aktuelle Zeit hinzu. Wenn das Netzwerk fehlschlägt, schlägt die Ausgabe einer Anforderung fehl.
Wahrscheinlich ist dies der Code, der detaillierte Code wird am Ende des Artikels veröffentlicht
Schauen Sie sich den Effekt an: Demo
Hier zeigen wir das vom normalen Netzwerk bis zum abnormalen Netzwerk und kehren dann zu normalen Situationen zurück.
Ende
Das obige Kapitel ist der gesamte Prozess von Ideen, Code und Renderings. Hier ist die Adresse der Demo. Wenn es Ihnen gefällt, können Sie auf Start klicken.
Demo -Adresse: https://github.com/xiaolei123/okhttpcacheInterceptor
Das obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, es wird für das Lernen aller hilfreich sein und ich hoffe, jeder wird Wulin.com mehr unterstützen.