المقدمة: منذ بعض الوقت ، عند تطوير التطبيقات ، غالبًا ما لم أتمكن من الحصول على البيانات التي تم الحصول عليها من الشبكة بسبب بيئة جهاز المستخدم ، وبالتالي فإن النتائج المعروضة على التطبيق هي دائمًا مربع فارغ. هذا الموقف سيء للغاية لتجربة المستخدم. لذلك ، فكرت بجد وقررت أن أبدأ بـ OKHTTP (لأن إطار طلب الشبكة الذي استخدمته في المشروع هو OKHTTP) ، وكتبت هذا اعتراض ذاكرة التخزين المؤقت لبيانات الشبكة.
حسنًا ، ثم قررنا البدء في الكتابة. اسمحوا لي أولاً أن أتحدث عن أفكاري:
الأفكار
نظرًا لأننا نكتب عن اعتراضات ذاكرة التخزين المؤقت لبيانات الشبكة ، والتي تستخدم بشكل أساسي وظيفة التقاطع القوية لـ OKHTTP ، ما هي البيانات التي يجب علينا تخزينها ، أو في أي ظروف يجب أن نمكّن آلية ذاكرة التخزين المؤقت للبيانات؟
أولاً: يتم دعم طلبات النشر ، لأن المسؤول قد قدم اعتراضًا ذاكرة التخزين المؤقت ، لكن أحد العيوب هو أنه يمكنهم فقط تخزين البيانات المطلوبة عن طريق GET ، لكنهم لا يدعمون المنشور.
ثانياً: عندما تكون الشبكة طبيعية ، فإن جلب البيانات من الشبكة. إذا كانت الشبكة غير طبيعية ، مثل TimeOutException UnnowhostException والمشاكل الأخرى ، فإننا نحتاج إلى ذاكرة التخزين المؤقت واسترداد البيانات.
ثالثًا: إذا كانت البيانات التي تم جلبها من ذاكرة التخزين المؤقت فارغة ، فلا نزال بحاجة إلى ترك هذا الطلب يمر عبر العملية العادية المتبقية.
رابعًا: يجب أن يكون لدى المتصل سيطرة كاملة على آلية التخزين المؤقت ويمكن أن يقرر بشكل انتقائي ما إذا كان سيتم تخزين البيانات وفقًا لاحتياجات عمله.
خامسًا: يجب أن يكون الأمر بسيطًا للاستخدام ، وهي النقطة الأكثر أهمية.
حسنًا ، لقد أدرجنا خمس نقاط أعلاه ، وهي أفكارنا العامة. دعنا نتحدث عن جزء الكود الآن:
مقالة رمز
إطار التخزين المؤقت: إطار ذاكرة التخزين المؤقت التي أستخدمها هنا هو disklrucache https://github.com/jakewharton/disklrucache يمكن تخزين إطار ذاكرة التخزين المؤقت هذا محليًا وتمت الموافقة عليه بواسطة Google. هذا هو أيضا السبب الرئيسي لاختيار هذا الإطار. أقوم أيضًا بتغليف فئة CacheManager إلى إطار ذاكرة التخزين المؤقت:
استيراد Android.content.context ؛ استيراد Android.content.pm.packageinfo ؛ استيراد Android.content.pm.packagemanager ؛ استيراد com.xiaolei.okhttpcacheintroceptor.log.log java.io.ioException ؛ import java.io.outputstream ؛ import java.io.unsupportedencodingexception ؛ import java.security.messagedigest ؛ import java.security.nosuchalgorithmexception ؛/*** التي تم إنشاؤها بواسطة xiaolei في 2017/5/17. */Class CACHEMANAGER {public static Final String Tag = "Cachemanager" ؛ // Max Cache Size 10MB SNIPENT FIND FONG DISK_CACHE_SIZE = 1024 * 10 ؛ خاص ثابت نهائي int disk_cache_index = 0 ؛ static final String Private CACHE_DIR = "Responses" ؛ disklrucache mdisklrucache ؛ خاص متطاير ثابت CacheManager McAcheManager ؛ Cachemanager public static getInstance (سياق السياق) {if (mcachemanager == null) {synchronized (cachemanager.class) {if (mcachemanager == null) {mcachemanager = new cachemanager (context) ؛ }}} return mcachemanager ؛ } cachemanager الخاص (سياق السياق) {file diskcachedir = getDiskCachedir (السياق ، 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/*كم عدد الملفات التي تتوافق مع مفتاح*/، disk_cache_size) ؛ log.d (tag ، "mdisklrucache created") ؛ } catch (ioException e) {E.PrintStackTrace () ؛ }}} / *** قم بتعيين ذاكرة التخزين المؤقت بشكل متزامن* / public void putCache (مفتاح السلسلة ، قيمة السلسلة) {if (mdisklrucache == null) return ؛ OutputStream OS = NULL ؛ حاول {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 () ؛ } أخيرًا {if (os! = null) {try {os.close () ؛ } catch (ioException e) {E.PrintStackTrace () ؛ }}}} / *** قم بتعيين ذاكرة التخزين المؤقت بشكل غير متزامن* / public void setCache (مفتاح السلسلة النهائية ، قيمة السلسلة النهائية) {new thread () {Override public void run () {putCache (المفتاح ، القيمة) ؛ } }.يبدأ()؛ } /*** متزامن getCache getCache (مفتاح السلسلة) {if (mdisklrucache == null) {return null ؛ } fileInputStream fis = null ؛ bytearrayoutputstream bos = null ؛ حاول {disklrucache.snapshot snapshot = mdisklrucache.get (Encryptmd (key)) ؛ if (snapshot! = null) {fis = (fileInputStream) snapshot.getInputStream (disk_cache_index) ؛ BOS = جديد bytearrayoutputStream () ؛ Byte [] buf = new byte [1024] ؛ int len ؛ بينما ((len = fis.read (buf))! = -1) {bos.write (buf ، 0 ، len) ؛ } byte [] data = bos.tobytearray () ؛ إرجاع سلسلة جديدة (بيانات) ؛ }} catch (ioException e) {E.PrintStackTrace () ؛ } أخيرًا {if (fis! = null) {try {fis.close () ؛ } catch (ioException e) {E.PrintStackTrace () ؛ }} if (bos! = null) {try {bos.close () ؛ } catch (ioException e) {E.PrintStackTrace () ؛ }}} return null ؛ } / *** بشكل غير متزامن getCache* / public void getCache (مفتاح السلسلة النهائية ، cachecallback callback) {new thread () {Override public void run () {String cache = getCache (key) ؛ callback.ongetcache (ذاكرة التخزين المؤقت) ؛ } }.يبدأ()؛ } / *** قم بإزالة ذاكرة التخزين المؤقت* / boolean public removeCache (مفتاح السلسلة) {if (mdisklrucache! = null) {try {return mdisklroucache.remove (incryptmd5 (key)) ؛ } catch (ioException e) {E.PrintStackTrace () ؛ }} إرجاع خطأ ؛ } / *** الحصول على دليل ذاكرة التخزين المؤقت* / ملف خاص getDiskCachedir (سياق السياق ، سلسلة uniquename) {string cachepath = context.getCachedir (). getPath () ؛ إرجاع ملف جديد (cachepath + file.separator + uniquename) ؛ } / *** MD5 ترميز السلسلة* / السلسلة الثابتة العامة ENCRYPTMD5 (سلسلة السلسلة) {try {byte [] hash = messagedigest.getInstance ("md5"). digest (string.getBytes ("utf-8")) ؛ StringBuilder hex = new StringBuilder (hash.length * 2) ؛ لـ (byte b: hash) {if ((b & 0xff) <0x10) {hex.append ("0") ؛ } hex.append (integer.toHexString (b & 0xff)) ؛ } إرجاع hex.toString () ؛ } catch (nosuchalgorithMexception | UnsupportedEncodingException e) {E.PrintStackTrace () ؛ } سلسلة الإرجاع ؛ } / *** الحصول على رقم إصدار التطبيق* / private int getAppversion (سياق السياق) {packagemanager pm = context.getPackageManager () ؛ حاول {packageInfo pi = pm.getPackageInfo (context.getPackagename () ، 0) ؛ إرجاع pi == فارغ؟ 0: pi.versioncode ؛ } catch (packagemanager.namenotfoundException e) {E.PrintStackTrace () ؛ } العودة 0 ؛ }} اعتراض Cacheinterceptor: يستخدم آلية اعتراض اعتراض OKHTTP للحكم بذكاء على سيناريوهات ذاكرة التخزين المؤقت وظروف الشبكة ، والتعامل مع سيناريوهات مختلفة.
استيراد Android.content.context ؛ استيراد com.xiaolei.okhttpcacheinterceptor.catch.cachemanager ؛ استيراد com.xiaolei.okhttpcacheintrocep.log.log OKHTTP3.Request ؛ استيراد OKHTTP3.RESPONSE ؛ استيراد OKHTTP3.RESPONSEBODY ؛/*** CACHE CLASS من سلسلة* تم إنشاؤها بواسطة Xiaolei في 2017/12/9. */CACHENTEPTOR الفئة العامة تنفذ اعتراض {سياق السياق الخاص ؛ public void setContext (سياق السياق) {this.context = context ؛ } public cacheinterceptor (سياق السياق) {this.context = context ؛ } Override Public Response Intercept (سلسلة) يلقي ioException {request request = chain.request () ؛ سلسلة cachehead = request.header ("ذاكرة التخزين المؤقت") ؛ String cache_control = request.header ("Cache-Control") ؛ إذا كان ("true" .equals (CacheHead) || // فهذا يعني cache (cache_control! = null &&! cache_control.isempty ())) url url = request.url (). url (). toString () ؛ سلسلة استجابة = فارغة ؛ سلسلة reqbodystr = getPostParams (طلب) ؛ حاول {استجابة الاستجابة = chain.proceed (طلب) ؛ إذا تم تنفيذ (Response.issucessful ()) // معالجة ذاكرة التخزين المؤقت فقط بعد إرجاع طلب الشبكة بنجاح. خلاف ذلك ، يتم تخزين 404 في ذاكرة التخزين المؤقت. أليست مزحة؟ {reponseBody ResponseBody = response.body () ؛ if (responseBody! = null) {deponstest = responseBody.String () ؛ if (responstr == null) {reversestr = "" ؛ } cachemanager.getInstance (Context) .SetCache (cachemanager.encryptmd5 (url + reqbodystr) ، desponsester) ؛ // تخزين ذاكرة التخزين المؤقت ، استخدم المعلمة + المعلمة إلى MD5 تشفير as storage chog.i ("httpretrofit" ، "-> الضغط cache:" + url + " } إرجاع getOnlineresponse (استجابة ، ريفستر) ؛ } آخر {return chain.proceed (request) ؛ }} catch (استثناء e) {استجابة الاستجابة = getCacherEpronse (request ، oldnow) ؛ // حدث استثناء ، بدأت في ذاكرة التخزين المؤقت هنا ، لكن قد لا يتم تخزينها مؤقتًا ، لذلك أحتاج إلى رميها إلى الجولة التالية من المعالجة لفترة طويلة إذا (الاستجابة == null) {return chain.proceed (request) ؛ // drop to the to to to to to to the the the to processing} {return response ؛ }}} آخر {return chain.proceed (request) ؛ }} استجابة خاصة getCacheresponse (طلب طلب ، oldnow طويل) {log.i ("httpretrofit" ، "-> حاول الحصول على ذاكرة التخزين المؤقت ---------") ؛ url url = request.url (). url (). toString () ؛ سلسلة params = getPostParams (طلب) ؛ سلسلة cachestr = cachemanager.getInstance (السياق) .getCache (cachemanager.encryptmd5 (url + params)) ؛ // الحصول على ذاكرة التخزين المؤقت ، استخدم معلمات الارتباط + لترميز md5 إلى مفتاح الحصول على مخيط ----------- العودة لاغية. } استجابة الاستجابة = استجابة جديدة. long useTime = System.CurrentTimeMillis () - OldNow ؛ log.i ("httpretrofit" ، "<- get cache:" + response.code () + "" + response.message () + "" + url + "(" + useTime + "MS)) ؛ log.i ("httpretrofit" ، cachestr + "") ؛ استجابة العودة ؛ } استجابة خاصة getOnlinerEpsonse (استجابة الاستجابة ، سلسلة السلسلة) {استجابة استجابة = استجابة. إرجاع استجابة جديدة. } /*** احصل على وضع النشر. المعلمات المرسلة إلى الخادم * * param request * return */ private string getPostParams (طلب طلب) {String reqbodystr = "" ؛ طريقة السلسلة = request.method () ؛ if ("post" .equals (method)) // إذا كان ذلك ، فاجعل كل معلمة قدر الإمكان {StringBuilder sb = new StringBuilder () ؛ if (request.body () مثيل 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)). } sb.delete (sb.length () - 1 ، sb.length ()) ؛ } reqbodystr = sb.toString () ؛ sb.delete (0 ، sb.length ()) ؛ }} return reqbodystr ؛ }}ما سبق هو الفكرة الرئيسية ورمز التنفيذ الرئيسي. دعنا نتحدث عن طريقة الاستخدام الآن
كيفية استخدام:
يستخدم Gradle:
ترجمة "com.xiaolei: OkhttpCacheInterceptor: 1.0.0"
نظرًا لأنه تم تقديمه للتو إلى JCenter ، فقد لا يكون قادرًا على سحبه لأسفل (لم تتم مراجعته بعد). يمكن للقارئ القلق إضافة رابط Maven الخاص بي إلى المستودعات في مشروعك: Build.gradle:
allprojects {repositories {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}ننشئ مشروعًا جديدًا ، وشاشة المشروع هي كما يلي:
لقطة شاشة المشروع
العرض التجريبي بسيط للغاية ، صفحة رئيسية واحدة ، حبة واحدة ، تحديثية واحدة ، واجهة طلب شبكة واحدة
لاحظ أنه نظرًا لأنها شبكة وذاكرة التخزين المؤقت والعلاقة ذات الصلة ، فلا شك أننا بحاجة إلى إضافة أذونات طلب الشبكة وأذونات قراءة الملفات وكتابةها في البيان:
<use-permission android: name = "Android.permission.internet" /> <use-permission android: name = "android.permission.read_external_storage" /> <use-permission android: name = "Android.Permission.write_sternal_storage" />
عند استخدامه ، تحتاج فقط إلى إضافة اعتراض إلى OKHTTPClient:
client = new OKHTTPCLIENT.BUILDER () .ADDINTEPTOR (مقبول ذاكرة التخزين المؤقت الجديد (السياق)) // إضافة اعتراض ذاكرة التخزين المؤقت ، أضف دعم ذاكرة التخزين المؤقت. retryonconnectionfailure (صواب) // فشل Reconnect.ConnectTimeOut (30 ، timunit.seconds) // وحدة طلب الشبكة هي الثانية () ؛
إذا كنت ترغب في تخزين بيانات الواجهة ، في ثم لفترة طويلة ، أضف رأس طلب لواجهة الشبكة الخاصة بك. تحتوي فئة CacheHeaders.Java على جميع المواقف. بشكل عام ، هناك حاجة فقط إلى ذرة الخزائن.
الواجهة العامة net { @headers (cacheHeaders.Normal) // هنا هي المفتاح @formurlencoded post ("geocoding") المكالمة العامة <TaBean> getIndex (field ("a") السلسلة A) ؛}رمز العمل:
net net = retrofitbase.getRetRofit (). إنشاء (net.class) ؛ استدعاء <CataBean> call = net.getIndex ("Suzhou City") ؛ call.enqueue (رد اتصال جديد <TaBean> () {Override public void onResponse (call <vatabean> call ، strappion <DataBean> repose) {dataBean data = response.body () onFailure (استدعاء <CataBean> ، رمي t) {textView.settext ("فشل الطلب!") ؛إذا نجح طلب الشبكة لدينا ، فسنقوم بإخراج النص على الواجهة ، وإضافة الوقت الحالي. إذا فشلت الشبكة ، فإن إخراج الطلب يفشل.
ربما هذا هو الرمز ، وسيتم نشر الرمز التفصيلي في نهاية المقالة
انظر إلى التأثير: العرض التوضيحي
نوضح هنا أنه من الشبكة العادية إلى الشبكة غير الطبيعية ، ثم العودة إلى المواقف العادية.
النهاية
الفصل أعلاه هو العملية بأكملها من الأفكار والرمز ، ثم إلى العروض. هنا هو عنوان العرض التوضيحي. إذا أعجبك ذلك ، يمكنك النقر فوق "ابدأ".
العنوان التجريبي: https://github.com/xiaolei123/okhttpcacheinterceptor
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.