序文:しばらく前に、アプリを開発するとき、ユーザーのデバイス環境のためにネットワークからデータを取得できなかったことが多いため、アプリに表示される結果は常に空白のボックスです。この状況は、ユーザーエクスペリエンスにとって非常に悪いです。したがって、私は一生懸命考えて、okhttpから始めることにしました(プロジェクトで使用したネットワーク要求フレームワークはokhttpであるため)、そのようなネットワークデータキャッシュインターセプターを書きました。
わかりました、それから私たちは書き始めることにしました。まず私のアイデアについて話させてください:
アイデア
主にokhttpの強力なインターセプター関数を利用するネットワークデータキャッシュインターセプターについて書いているので、どのデータをキャッシュする必要がありますか、またはどのような状況でデータキャッシュメカニズムを有効にする必要がありますか?
まず:役人はキャッシュインターセプターを提供しているため、投稿リクエストがサポートされていますが、1つの欠点は、Getが要求したデータのみをキャッシュできますが、投稿をサポートしていないことです。
2番目:ネットワークが正常な場合、ネットワークからデータを取得することです。 Timeoutexception Unknowhostexceptionやその他の問題など、ネットワークが異常である場合、データをキャッシュして取得する必要があります。
第三:キャッシュから取得されたデータが空の場合、この要求を残りの通常のプロセスを実行する必要があります。
第四:発信者は、キャッシュメカニズムを完全に制御する必要があり、ビジネスニーズに応じてデータをキャッシュするかどうかを選択的に決定できます。
5番目:使用が簡単でなければなりません。これは最も重要なポイントです。
OK、私たちは5ポイントを上にリストしました。これは私たちの一般的なアイデアです。今すぐコード部分について話しましょう:
コード記事
キャッシュフレームワーク:ここで使用するキャッシュフレームワークは、disklrucache https://github.com/jakewharton/disklrucacheこのキャッシュフレームワークをローカルに保存し、Googleによって承認されています。これは、このフレームワークを選択する主な理由でもあります。また、CacheManagerクラスをキャッシュフレームワークにカプセル化します。
Android.content.context; Import android.content.pm.packageInfo; Import android.content.pm.packagemanager; Import com.xiaolei.okhttpcacheintector.log.log; Import java.io.io.bytearrayOutputstream; inmol.file.file.file.fileのインポート; java.io.ioexception; Import java.io.outputStream; Import java.io.unsupportedencodingception; Import java.security.messagegest; Import java.security.nosuchalgorithmexception;/*** xiaoleiが2017/5/17に作成しました。 */public class cachemanager {public static final string tag = "cachemanager"; //最大キャッシュサイズ10MBプライベートスタティック最終long disk_cache_size = 1024 * 10;プライベート静的最終int disk_cache_index = 0;プライベート静的最終文字列cache_dir = "responses"; private disklrucache mdisklrucache;民間の揮発性静的CACHEMANAGER McACHEMANAGER; public static cachemanager getInstance(コンテキストコンテキスト){if(mcachemanager == null){synchronized(cachemanager.class){if(mcachemanager == null){mcachemanager = new cachemanager(context); }}} return mcachemanager; } private cachemanager(コンテキストコンテキスト){file diskcachedir = getDiskCachedir(Context、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(コンテキスト)、1/*キー*/、disk_cache_size); log.d(tag、 "mdisklrucache created"); } catch(ioexception e){e.printstacktrace(); }}} / ***同期セットキャッシュ* / public void putcache(string key、string value){if(mdisklrucache == null)return; outputStream os = null; try {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(final string key、final string value){new shood(){@override public void run(){putcache(key、value); } }。始める(); } /*** synchronally 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(); byte [] buf = new byte [1024]; int len; while((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(); }}} nullを返します。 } / *** ASYNCHRONINALY GETCACHE* / public void getCache(最終文字列キー、最終Cachecallbackコールバック){new Thread(){@Override public void run(){string cache = getCache(key); callback.ongetcache(キャッシュ); } }。始める(); } / ***キャッシュを削除* / public boolean removecache(string key){if(mdisklrucache!= null){try {return mdisklrucache.remove(encryptmd5(key)); } catch(ioexception e){e.printstacktrace(); }} falseを返します。 } / *** get cache directory* / private file getDiskCachedir(Context Context、String uniquename){string cachepath = context.getCachedir()。getPath();新しいファイル(cachepath + file.separator + uniquename)を返します。 } / *** md5文字列のエンコーディング* / public static string encryptmd5(string string){try {byte [] hash = mesagedigest.getInstance( "md5")。 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(); } return string; } / ***アプリバージョン番号を取得* / private int getAppversion(コンテキストコンテキスト){packagemanager pm = context.getPackageManager(); try {packageInfo pi = pm.getPackageInfo(context.getPackagename()、0); pi == nullを返しますか? 0:pi.versioncode; } catch(packagemanager.namenotfoundexception e){e.printstacktrace(); } return 0; }} CacheInterceptorインターセプター: OkhTTPのインターセプターインターセプターメカニズムを使用して、キャッシュシナリオとネットワーク条件をインテリジェントに判断し、さまざまなシナリオを処理します。
Android.content.contextのインポート; com.xiaolei.okhttpcacheinterceptor.catch.cachemanagerをインポート; com.xiaolei.okhttpcacheinterceptor.log.log;インポートjava.io.io.ioexception; Import okhttp3.form; import okhttp3.protocol; Import okhttp3.protocol; okhttp3.request; Import okhttp3.response; Import okhttp3.responsebody;/*** cache of string* cache of string*は2017/12/9に作成されました。 */public class cacheinterceptorはインターセプターを実装します{プライベートコンテキストコンテキスト。 public void setContext(コンテキストコンテキスト){this.context = context; } public cacheinterceptor(コンテキストコンテキスト){this.context = context; } @Override public Response Intercept(チェーンチェーン)IoException {request request = Chain.Request();文字列cachehead = request.header( "cache");文字列cache_control = request.header( "cache-control"); if( "true" .equals(cachehead)|| //キャッシュ(cache_control!= null &&!cache_control.isempty())// web-sideプロトコルのキャッシュヘッダーもサポートします{long oldnow = system.currenttimemillis(); string url = request.url()。url()。toString();文字列応答= null; string reqbodystr = getpostparams(request); try {Response Response = Chain.ProCeed(リクエスト); if(respons.issuccessful())//キャッシュ処理は、ネットワークリクエストが正常に返された後にのみ実行されます。それ以外の場合、404はキャッシュに保存されます。冗談ではありませんか? {ResponseBody ResponseBody = respons.body(); if(responsebody!= null){ressionesth = responsebody.string(); if(resprtr == null){resspesterst = ""; } cachemanager.getInstance(context).setcache(cachemanager.encryptmd5(url + reqbodystr)、レスポンスト); //キャッシュを保存し、キーストレージlog.i( "httpretrofit"、 "、" + url + "); } getOnlineresponse(response、responsestre)を返します。 } else {return Chain.proceed(request); }} catch(Exception E){Response Response = getCacheresponse(Request、OldNow); //例外が発生し、ここでキャッシュを開始しましたが、キャッシュされていない可能性があるため、(Response == null){return chain.proceed(request); //処理の次のラウンドにドロップする} else {return response; }}} else {return Chain.proceed(request); }} private Response getCacheresponse(リクエストリクエスト、long oldnow){log.i( "httpretrofit"、 " - >キャッシュを取得してみてください---------"); string url = request.url()。url()。toString();文字列params = getpostparams(request); string cachestr = cachemanager.getInstance(context).getCache(cachemanager.encryptmd5(url + params)); // cache + parametersを使用して、md5へのエンコードを使用してキーにif(cachestr == null){log.i( "httpretrofit"、 " nullを返します。 }応答応答= new Response.builder().code(200).body(responsebody.create(null、cachestr)).request(request).message( "ok").protocol(protocol.http_1_0).build(); long usetime = system.currenttimemillis() - oldnow; log.i( "httpretrofit"、 "< - get cache:" + respons.code() + "" + respons.message() + "" + url + "(" + usetime + "ms)"); log.i( "httpretrofit"、cachestr + "");返信応答。 } private Response getonlineresponse(Response Response、string body){responsebody responsebody = response.body(); new Response.builder().code(respons.code()).body(responsebody.create(responsebody == null:responsebody.contenttype()、body)).request(respons.request()).message(response.message()).protocol(response.protocol().buil().buil().build().build().build()。 } /***ポストモードで取得します。サーバーに送信されたパラメーター * * @param request * @return */ private string getpostparams(request request){string reqbodystr = ""; string method = request.method(); if( "post" .equals(method))// postの場合、各パラメーターを可能な限り解析する{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( "=")。 } sb.delete(sb.length()-1、sb.length()); } reqbodyStr = sb.toString(); sb.delete(0、sb.length()); }} reqbodystrを返します。 }}上記は、主なアイデアであり、主な実装コードです。今すぐ使用方法について話しましょう
使い方:
Gradleは使用します:
コンパイル 'com.xiaolei:okhttpcacheinterceptor:1.0.0'
JCenterに提出されたばかりなので、それを引き下げることができないかもしれません(まだレビューされていません)。不安な読者は、プロジェクトのリポジトリに私のmavenリンクを追加できます:build.gradle:
AllProjects {repositries {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}}新しいプロジェクトを作成し、プロジェクトのスクリーンショットは次のとおりです。
プロジェクトスクリーンショット
デモは非常にシンプルで、1つのメインページ、1つのビーン、1つのレトロフィット、1つのネットワークリクエストインターフェイスがあります
ネットワーク、キャッシュ、および関連であるため、ネットワーク要求のアクセス許可を追加し、マニフェストに読み取りおよび書き込み許可をファイルする必要があることは間違いありません。
<uses-permission android:name = "android.permiss.internet" /> <ouses-permission-permiss android:name = "android.permisse.read_external_storage" /> <uses-permess android:name = "android.permission.write_external_storage" />
それを使用する場合、okhttpclientにインターセプターを追加する必要があります。
client = new okhttpclient.builder().addinterceptor(new cacheinterceptor(context))//キャッシュインターセプターの追加、cache support.(true)// failed reconnect.connecttimeout(30、timeunit.seconds)
どのインターフェイスのデータをキャッシュする場合は、長い間、ネットワークインターフェイスのリクエストヘッダーを追加します。 cacheheaders.javaクラスには、すべての状況が含まれています。一般に、キャッシュヘッダーのみが必要です。
public interface net {@headers(cacheheaders.normal)//ここにkey @formurencoded @post( "geocoding")public call <databean> getindex(@field( "a")string a);}ビジネスコード:
net net = retrofitbase.getretrofit()。create(net.class); <databean> call = net.getIndex( "Suzhou City"); call.enqueue(new callback <databean>(){@Override public void onResponse(call <databean> call、response <databean> response){databean data = respons.body(); textview.settext(date.getMinutes() + "" " + dateSeconds( onfailure(call <databean> call、throwable t){textView.settext( "request Failed!");ネットワークリクエストが成功した場合、インターフェイスにテキストを出力し、現在の時間を追加します。ネットワークが失敗した場合、リクエストの出力が失敗します。
おそらくこれはコードであり、詳細なコードは記事の最後に投稿されます
効果を見てください:デモ
ここでは、通常のネットワークから異常なネットワークまで、そして通常の状況に戻ることを示します。
エンディング
上記の章は、アイデア、コード、そしてレンダリングまでのプロセス全体です。これがデモのアドレスです。気に入ったら、[開始]をクリックできます。
デモアドレス:https://github.com/xiaolei123/okhttpcacheinterceptor
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。