Pendahuluan: Beberapa waktu yang lalu, ketika mengembangkan aplikasi, saya sering tidak bisa mendapatkan data yang diperoleh dari jaringan karena lingkungan perangkat pengguna, sehingga hasil yang ditampilkan pada aplikasi selalu merupakan kotak kosong. Situasi ini sangat buruk untuk pengalaman pengguna. Oleh karena itu, saya berpikir keras dan memutuskan untuk memulai dengan OKHTTP (karena Kerangka Permintaan Jaringan yang saya gunakan dalam proyek ini adalah OKHTTP), dan saya menulis Interceptor Cache Data Jaringan seperti itu.
Oke, lalu kami memutuskan untuk mulai menulis. Biarkan saya pertama kali berbicara tentang ide -ide saya:
Ide
Karena kami menulis tentang pencegat cache data jaringan, yang terutama memanfaatkan fungsi pencegat yang kuat dari OKHTTP, data apa yang harus kami cache, atau dalam keadaan apa kami harus memungkinkan mekanisme cache data?
Pertama: Permintaan POST didukung, karena pejabat telah memberikan pencegat cache, tetapi satu kerugian adalah bahwa mereka hanya dapat menyimpan data yang diminta oleh GET, tetapi mereka tidak mendukung postingan.
Kedua: Ketika jaringan normal, itu untuk mengambil data dari jaringan. Jika jaringan tidak normal, seperti timeoutexception UnknowHostException dan masalah lainnya, maka kita perlu menyimpan dan mengambil data.
Ketiga: Jika data yang diambil dari cache kosong, maka kita masih perlu membiarkan permintaan ini melewati proses normal yang tersisa.
Keempat: Penelepon harus memiliki kendali penuh atas mekanisme caching dan dapat secara selektif memutuskan apakah akan men -cache data sesuai dengan kebutuhan bisnisnya.
Kelima: Itu harus mudah digunakan, yang merupakan poin terpenting.
Oke, kami telah mendaftarkan lima poin di atas, yang merupakan ide umum kami. Mari kita bicara tentang bagian kode sekarang:
Artikel Kode
Kerangka Caching: Kerangka cache yang saya gunakan di sini adalah disklrucache https://github.com/jakewharton/disklrucache Kerangka kerja cache ini dapat disimpan secara lokal dan telah disetujui oleh Google. Ini juga merupakan alasan utama untuk memilih kerangka kerja ini. Saya juga merangkum kelas CacheManager dengan kerangka cache:
import android.content.context; import android.content.pm.packageInfo; import android.content.pm.packagemanager; import com.xiaolei.okhttpcacheinteptor.log.log; import java.bytearrayoutputstream; impor java.io.io.io.io. java.io.ioException; impor java.io.outputStream; impor java.io.unsupportedencodingException; impor java.security.messagedigest; impor java.security.noSuchalgorithMexception;/*** dibuat oleh Xiaolei pada 2017/5/5/5/57. */kelas publik CacheManager {public static final string tag = "cacheManager"; // Max Cache Ukuran 10MB Private Static Final Long Disk_cache_size = 1024 * 10; private static final int disk_cache_index = 0; string final statis private cache_dir = "responses"; Disklrucache Private Mdisklrucache; Private Volatile Static Cachemanager McAchemanager; Public Static Cachemanager getInstance (konteks konteks) {if (mcachemanager == null) {disinkronkan (cachemanager.class) {if (mcachemanager == null) {mcachemanager = cachemanager baru (konteks); }}} return mcacheManager; } private cacheManager (konteks konteks) {file diskcachedir = getDiskcachedir (konteks, cache_dir); if (! diskcachedir.exists ()) {boolean b = diskcachedir.mkdir (); Log.d (tag, "! Diskcachedir.exists () --- diskcachedir.mkdir () =" + b); } if (diskcachedir.getusablespace ()> disk_cache_size) {coba {mdisklrucache = disklrucache.open (diskcachedir, getAppversion (konteks), 1/*berapa banyak file yang sesuai dengan kunci*/, disk_cache_size); Log.d (tag, "mdisklrucache dibuat"); } catch (ioException e) {e.printstacktrace (); }}} / *** Secara sinkron set cache* / public void putcache (tombol string, nilai string) {if (mdisklrucache == null) return; OutputStream OS = null; coba {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 (); } akhirnya {if (os! = null) {coba {os.close (); } catch (ioException e) {e.printstacktrace (); }}}} / *** Secara asinkron set cache* / public void setCache (tombol string akhir, nilai string akhir) {thread baru () {@Override public void run () {putcache (key, value); } }.awal(); } /*** secara sinkron getCache getCache (tombol string) {if (mdisklrucache == null) {return null; } FileInputStream fis = null; BytearrayoutputStream bos = null; coba {disklrucache.snapshot snapshot = mdisklrucache.get (encryptmd5 (key)); if (snapshot! = null) {fis = (fileInputStream) snapshot.getInputStream (disk_cache_index); BOS = ByTeArrayOutputStream () baru; byte [] buf = byte baru [1024]; int len; while ((len = fis.read (buf))! = -1) {bos.write (buf, 0, len); } byte [] data = bos.tobytearray (); mengembalikan string baru (data); }} catch (ioException e) {E.PrintStackTrace (); } akhirnya {if (fis! = null) {coba {fis.close (); } catch (ioException e) {e.printstacktrace (); }} if (bos! = null) {coba {bos.close (); } catch (ioException e) {e.printstacktrace (); }}} return null; } / *** secara asinkron getCache* / public void getCache (tombol string akhir, callback cachecallback akhir) {utas baru () {@Override public void run () {string cache = getCache (key); callback.ongetcache (cache); } }.awal(); } / *** Hapus cache* / public boolean removecache (tombol string) {if (mdisklrucache! = Null) {coba {return mdisklrucache.remove (encryptmd5 (key)); } catch (ioException e) {e.printstacktrace (); }} return false; } / *** Dapatkan direktori cache* / file privat getDiskCachedir (konteks konteks, string uniquename) {string cachePath = context.getCachedir (). GetPath (); Mengembalikan file baru (CachePath + file.separator + uniquename); } / *** MD5 Pengkodean string* / public static string encryptMd5 (string string) {try {byte [] hash = messageSageT.getInstance ("md5"). Digest (string.getbytes ("utf-8"))); StringBuilder hex = new StringBuilder (hash.length * 2); untuk (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; } / *** Dapatkan nomor versi aplikasi* / int private int getAppversion (konteks konteks) {packageManager pm = context.getPackAgeManager (); coba {packageInfo pi = pm.getPackageInfo (context.getPackagename (), 0); return pi == null? 0: pi.versionCode; } catch (packageManager.namenotfoundException e) {e.printstacktrace (); } return 0; }} Cacheinterceptor Interceptor: Menggunakan mekanisme pencegat interceptor OkHTTP untuk secara cerdas menilai skenario cache dan kondisi jaringan, dan menangani berbagai skenario.
impor android.content.context; import com.xiaolei.okhttpcacheintepor.catch.cachemanager; import com.xiaolei.okhttpcacheinterceptor.log.log; import java.io.intepception; import.MPORTP.FORMBOD; OKHTTP3.REQUEST; Impor okhttp3.response; import okhttp3.responseBody;/*** Cache Class of String* Dibuat oleh Xiaolei pada 2017/12/9. */kelas publik CacheInterceptor mengimplementasikan Interceptor {Private Context Context; public void setContext (konteks konteks) {this.context = konteks; } public cacheinterceptor (konteks konteks) {this.context = konteks; } @Override Public Response Intercept (rantai rantai) melempar ioException {request request = chain.Request (); String cachehead = request.header ("cache"); String cache_control = request.header ("cache-control"); if ("true" .Equals (CacheHead) || // Ini berarti cache (CACHE_CONTROL! = NULL &&! CACHE_CONTROL.ISEMPTY ()) // Ini juga mendukung header cache untuk protokol sisi web {long oldnow = System.currentTimeMillis (); String url = request.url (). Url (). Tostring (); String responseStr = null; String reqbodystr = getPostParams (permintaan); coba {response response = chain.proed (request); if (response.issuccessful ()) // Cache Processing dilakukan hanya setelah permintaan jaringan kembali berhasil. Kalau tidak, 404 disimpan dalam cache. Bukankah itu lelucon? {Responsebody responseBody = response.body (); if (responseBody! = null) {responseStr = responseBody.string (); if (responstr == null) {responseStr = ""; } Cachemanager.getInstance (konteks) .setCache (cacheManager.encryptmd5 (url + reqbodystr), responseStr); // penyimpanan cache, gunakan tautan + parameter ke pengkodean md5 sebagai log penyimpanan utama. } return getOnlineresponse (respons, responseStr); } else {return chain.proed (request); }} catch (exception e) {response response = getCachereSponse (request, oldnow); // Pengecualian terjadi, saya mulai menempel di sini, tetapi mungkin tidak di -cache, jadi saya perlu melemparkannya ke putaran pemrosesan berikutnya untuk waktu yang lama jika (respons == null) {rantai pengembalian.proceed (permintaan); // jatuh ke putaran pemrosesan berikutnya} else {respons return; }}} else {return chain.proed (request); }} respons privat getCachereSponse (permintaan permintaan, lama lama) {log.i ("httpretrofit", "-> mencoba mendapatkan cache ---------"); String url = request.url (). Url (). Tostring (); String params = getPostParams (permintaan); String cacheStr = CacheManager.getInstance(context).getCache(CacheManager.encryptMD5(url + params));//Get cache, use link + parameters to MD5 encoding to KEY to get if (cacheStr == null) { Log.i("HttpRetrofit", "<-- Get Cache Failure -----------"); kembali nol; } Respons respons = response new.builder () .code (200) .body (responsbody.create (null, cachestr)) .request (permintaan) .message ("ok") .protocol (protocol.http_1_0) .build (); long exetime = system.currentTimemillis () - oldnow; Log.i ("httpretrofit", "<- dapatkan cache:" + response.code () + "" + response.message () + "" + url + "(" + exetime + "ms)"); Log.i ("httpretrofit", cachestr + ""); respons pengembalian; } Private Response getOnlineresponse (respons response, string body) {responsebody responseBody = response.body (); return new response.builder () .code (response.code ()) .body (responseBody.create (responseBody == null? null: responseBody.contentType (), body)) .restquest (response.request ()) .message (response.message ()) .protocol (response.rotocol (). } /*** Dapatkan dalam mode postingan. Parameter yang dikirim ke server * * @param permintaan * @return */ private string getPostParams (permintaan permintaan) {string reqbodystr = ""; Metode String = request.method (); if ("Post" .Equals (Metode)) // Jika postingan, parse setiap parameter sebanyak mungkin {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; }}Di atas adalah ide utama dan kode implementasi utama. Mari kita bicara tentang metode penggunaan sekarang
Bagaimana menggunakan:
Lulusan menggunakan:
Compile 'com.xiaolei: okhttpcacheinterceptor: 1.0.0'
Karena baru saja diserahkan ke JCenter, mungkin tidak dapat menariknya (belum ditinjau). Pembaca yang cemas dapat menambahkan tautan Maven saya ke repositori dalam proyek Anda: build.gradle:
AllProjects {repositories {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}Kami membuat proyek baru, dan screenshot proyek adalah sebagai berikut:
Tangkapan layar proyek
Demo sangat sederhana, satu halaman utama, satu kacang, satu retrofit, satu antarmuka permintaan jaringan
Perhatikan bahwa karena itu adalah jaringan, cache, dan terkait, tidak ada keraguan bahwa kita perlu menambahkan izin permintaan jaringan dan file membaca dan menulis izin dalam manifes:
<Use-Insmission Android: name = "android.permission.internet" /> <use-izin android: name = "android.permission.read_external_storage" /> <use-persyaratan android: name = "android.permission.write_external_storage" / />
Saat menggunakannya, Anda hanya perlu menambahkan interseptor ke okhttpclient Anda:
Client = new OKHTTPCLIENT.BUILDER () .AdDInterceptor (CacheInterceptor baru (konteks)) // Tambahkan Cache Interceptor, tambahkan cache dukungan.RetryOnConnectionFailure (true) // gagal reconnect.connecttimeout (30, timeunit.seconds) // Unit Timeout Network Reconnect.connectTimeOut (30, timeunit.seconds) // Unit Timeout Network Request.
Jika Anda ingin cache data antarmuka mana, maka untuk waktu yang lama, tambahkan header permintaan untuk antarmuka jaringan Anda. Kelas CacheHeaders.java berisi semua situasi. Umumnya, hanya cacheeheaders. Normal diperlukan.
antarmuka publik net {@headers (cacheHeaders.normal) // Berikut ini adalah kunci @FormUrlencoded @post ("geocoding") panggilan publik <databean> getIndex (@field ("a") string a);}Kode Bisnis:
Net net = retrofitbase.getretrofit (). Create (net.class); Hubungi <databean> call = net.getIndex ("Suzhou City"); call.enqueue (callback baru <databean> () {@Override public void onResponse (hubungi <databean> panggilan, respons <databean> respons) {databean data = response.body (); tanggal = date new (); textView.setText (date.getMinutes () + "" date.getSececconds void onFailure (hubungi <databean> panggilan, lempar t) {textview.settext ("Permintaan gagal!");Jika permintaan jaringan kami berhasil, kami akan mengeluarkan teks pada antarmuka, dan menambahkan waktu saat ini. Jika jaringan gagal, output dari suatu permintaan gagal.
Mungkin ini kodenya, kode terperinci akan diposting di akhir artikel
Lihat efeknya: demo
Di sini kami menunjukkan bahwa dari jaringan normal ke jaringan abnormal, dan kemudian kembali ke situasi normal.
Akhir
Bab di atas adalah seluruh proses dari ide, kode, dan kemudian untuk rendering. Inilah alamat demo. Jika Anda menyukainya, Anda dapat mengklik Mulai.
Alamat demo: https://github.com/xiaolei123/okhttpcacheinterceptor
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.