ПРЕДИСЛОВИЕ: Некоторое время назад, при разработке приложений, я часто не мог получить данные, полученные из сети из -за среды устройства пользователя, поэтому результаты, отображаемые в приложении, всегда являются пустой коробкой. Эта ситуация чрезвычайно плохой для пользовательского опыта. Поэтому я подумал и решил начать с OKTTP (потому что структура сетевого запроса, которую я использовал в проекте, - OKTTP), и я написал такой сетевой перехватчик кэша данных.
Хорошо, тогда мы решили начать писать. Позвольте мне сначала поговорить о моих идеях:
Идеи
Поскольку мы пишем о перехватчиках кэша сетевых данных, которые в основном используют мощную функцию перехватчика OKTTP, какие данные мы должны кэшировать, или при каких обстоятельствах мы должны включить механизм кэша данных?
Во -первых, поддерживаются запросы на почту, потому что чиновник предоставил перехватчик кэша, но один недостаток заключается в том, что они могут только кэшировать данные, запрошенные GET, но они не поддерживают сообщение.
Второе: когда сеть нормальная, она должна извлечь данные из сети. Если сеть является ненормальной, такой как TimeoutException Unknowhostexception и другие проблемы, то нам нужно кэшировать и извлекать данные.
Третий: если данные, извлеченные из кэша, пусты, нам все равно нужно позволить этому запросу пройти оставшийся нормальный процесс.
В -четвертых: абонент должен иметь полный контроль над механизмом кэширования и может выборочно решить, следует ли кэшировать данные в соответствии с его или ее бизнесом.
Пятый: это должно быть просто в использовании, что является наиболее важным моментом.
Хорошо, мы перечислили пять баллов выше, которые являются нашими общими идеями. Давайте поговорим о кодовой части сейчас:
Кодовая статья
Структура кэширования: кеш -структура, которую я использую здесь, - это Disklrucache https://github.com/jakewharton/disklrucache. Эта структура кэша может быть сохранена локально и была одобрена Google. Это также главная причина выбора этой структуры. Я также инкапсулирую класс Cachemanager в рамку кэша:
Import Android.content.Context; Import Android.content.pm.packageinfo; import android.content.pm.packagemanager; import com.xiaolei.ohttpcacheinterceptor.log.log; импорт java.io.bytearrayoutputem java.io.ioexception; импорт java.io.outputstream; импорт java.io.unsupportedencodingexception; import java.security.messagedigest; импорт java.security.nosuchalgorithmexception;/*** Создан Xiaolei на 2017/5/17. */public Class Cachemanager {public Static Final String Tag = "cachemanager"; // максимальный размер кэша 10 МБ частный статический статический последний длинный DISK_CACHE_SIZE = 1024 * 10; Private Static Final int disk_cache_index = 0; Private Static Final String Cache_DIR = "Ответы"; Частный Disklrucache mdisklrucache; Частный летучий статический Cachemanager McAchemanager; public Static Cachemanager GetInstance (контекст контекста) {if (mcachemanager == null) {synchronized (cachemanager.class) {if (mcachemanager == null) {mcachemanager = new cachemanager (context); }}} вернуть 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 (context), 1/*Сколько файлов соответствует ключу*/,, disk_cache_size); Log.d (Tag, "mdisklrucache создан"); } 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 (конечная строка, конечная строка значения) {new Thread () {@Override public void run () {putcache (key, value); } }.начинать(); } /*** Синхронно 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 (); вернуть новую строку (data); }} 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; } / *** asynchronals getCache* / public void getCache (конечная строковая клавиша, окончательный обратный вызов cacheCallback) {new Thread () {@Override public void run () {string cache = getCache (Key); Callback.ongetCache (Cache); } }.начинать(); } / *** Удалить кэш* / public boolean removecache (String Key) {if (mdisklrucache! = Null) {try {return mdisklrucache.remove (encryptmd5 (key)); } catch (ioException e) {e.printstackTrace (); }} вернуть false; } / *** GET CACHE DICELECTORY* / private File getDiskCachedir (контекст контекста, string UniqueName) {String cachepath = context.getCachedir (). GetPath (); вернуть новый файл (cachepath + file.separator + Uniquenmeame); } / *** MD5 Кодирование строки* / public static String encryptmd5 (String String) {try {byte [] hash = messagedigest.getInstance ("md5"). Digest (string.getBytes ("utf-8")); StringBuilder hex = new StringBuilder (hash.length * 2); for (byte b: hash) {if ((b & 0xff) <0x10) {hex.append ("0"); } hex.Append (integer.tohexstring (b & 0xff)); } вернуть 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 Interceptor: использует механизм перехватчика OKTTP, чтобы разумно судить сценарии кэша и условия сети, и обрабатывать различные сценарии.
Import Android.content.context; Import Com.xiaolei.ohttpcacheinterceptor.catch.cachemanager; Import com.xiaolei.ohttpcacheinterceptor.log.log; импорт java.io.ioexception; import okhttp3.formbody; importp3 okhttp3.request; import okhttp3.response; import okhttp3.responsebody;/*** Класс кэша строки*, созданный Xiaolei 2017/12/9. */public Class Cacheinterceptor реализует Interceptor {частный контекст; public void setContext (контекст контекста) {this.context = context; } public cacheInterceptor (контекст контекста) {this.context = context; } @Override public response intercept (цепочка цепи) бросает ioException {request request = chain.request (); String cachehead = request.header ("cache"); String cache_control = request.header ("cache-control"); if ("true" .equals (cachehead) || // это означает кэш (cache_control! = null &&! cache_control.isempty ())) // Это также поддерживает заголовки кэша для протоколов веб-сайта {long oldnow = system.currenttimelis (); String url = request.url (). Url (). ToString (); String actrassestR = null; String reqbodystr = getPostParams (запрос); try {response response = chain.proceed (request); if (response.issuccessful ()) // Обработка кэша выполняется только после того, как сетевой запрос успешно возвращается. В противном случае 404 хранится в кэше. Разве это не шутка? {Ответ -обратный обратный ответ = response.body (); if (responsebody! = null) {responseStr = responsebody.string (); if (responstr == null) {responseStr = ""; } CachEmanager.getInstance (context) .setCache (cachemanager.encryptmd5 (url + reqbodystr), responsestr); // хранилище кэш, используйте параметр Link + для кодирования MD5 как ключевой журнал хранения ("httpretrofit", "-> push-cache:" + url + ": успешно"); } return getOnlineresponse (response, actrassestR); } else {return chain.proceed (request); }} catch (Exception e) {response response = getCacheresponse (запрос, OldNow); // произошло исключение, я начал кэшировать здесь, но оно не может быть кэшировано, поэтому мне нужно бросить его в следующий раунд обработки в течение длительного времени, если (response = null) {return chain.proceed (request); // снизиться до следующего раунда обработки} else {return response; }}} else {return chain.proecd (request); / String url = request.url (). Url (). ToString (); String params = getPostParams (запрос); String cachestr = cachemanager.getInstance (context) .getCache (cachemanager.encryptmd5 (url + params)); // Получить кэш, используйте ссылку + параметры для кодирования MD5 для ключа для получения if (cachestr == null) {log.i ("httpretrofit", "<-get cache awch ---------------"); вернуть ноль; } Response = 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:" + response.code () + "" + response.message () + "" + url + "(" + usetime + "ms)"); Log.i ("httpretrofit", cachestr + ""); ответный ответ; } Частный ответ getOnlineResponse (ответ ответ, корпус строки) {ответный ответ BeplyBody = response.body (); вернуть новый ответ.builder () .code (response.code ()) .body (responsebody.create (responsebody == null? null: responsebody.contenttype (), body)) .request (response.request ()) .message (response.message () .protocol (response.protocol ()); } /*** Получить в режиме сообщения. Параметры, отправленные на сервер * * @param запрос * @return */ private String getPostParams (запрос запроса) {string reqbodystr = ""; String method = request.method (); if ("post" .equals (method)) // Если это Post, проанализируйте каждый параметр как можно больше {StringBuilder sb = new StringBuilder (); if (request.body () exanceof 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; }}Выше всего является основной идеей и основным кодом реализации. Давайте поговорим о методе использования сейчас
Как использовать:
Градл использует:
Compile 'com.xiaolei: okhttpcacheinterceptor: 1.0.0'
Поскольку он только что был представлен в JCenter, он не сможет снять его (он еще не был рассмотрен). Тревожный читатель может добавить мою ссылку Maven в репозитории в вашем проекте: build.gradle:
AllProjects {Repositories {maven {url 'https://dl.bintray.com/kavipyouxiang/maven'}}}Мы создаем новый проект, а скриншот проекта выглядит следующим образом:
Проектный скриншот
Демо очень проста, одна главная страница, один боб, один модернизатор, один интерфейс запроса сети.
Обратите внимание, что, поскольку это сеть, кэш и связанный с этим, нет сомнений, что нам нужно добавить разрешения на запрос сети, а также разрешения на чтение и запись в файле:
<users-permission android: name = "android.permission.internet" /> <users-permission android: name = "android.permission.read_external_storage" /> <users-permission android: name = "android.permission.write_external_storage" />
При его использовании вам просто нужно добавить перехватчик в свой OkhttpClient:
client = new okhttpclient.builder () .addinterceptor (новый cacheinterceptor (context)) // Добавить кэш -перехватчик, добавить cache support.retryonconnectionfailure (true) // Неудача reconnect.connecttimeout (30, timeUnit.seconds) // Подразделение сетевого запроса сетевого времени - секунд.
Если вы хотите кэшировать данные о том, какой интерфейс, в течение длительного времени добавьте заголовок запроса для вашего сетевого интерфейса. Класс CacheHeaders.java содержит все ситуации. Как правило, требуется только кэш -хедеры. Нормальные.
Общедоступный интерфейс net {@headers (cacheheaders.normal) // Вот ключ @formurlencoded @post ("Geoocoding") public Call <tatabean> getIndex (@Field ("a") строка a);}Бизнес -код:
Net net = MOROFITBASE.GetReTrofit (). CREATION (NET.CLASS); CALL <TANABEAN> call = net.getIndex ("Suzhou City"); call.enqueue (новый обратный вызов <TATABEAN> () {@Override public void onResponse (call <TabeAn> Call, ответ <Databean> response) {Databean Data = response.body (); Date Date = new Date (); TextView.settext (date.getMinutes () + "" + date.getseconds () + ":/n hate") ")") ")") ")") OnFailure (Call <Databean> Call, Throwable T) {textView.settext ("Запрос не удастся!");Если наш сетевой запрос будет успешным, мы выведем текст в интерфейс и добавим текущее время. Если сеть сбой, вывод запроса не выполняется.
Наверное, это код, подробный код будет размещен в конце статьи
Посмотрите на эффект: демонстрация
Здесь мы демонстрируем, что от нормальной сети до аномальной сети, а затем возвращаемся к нормальным ситуациям.
Конец
Приведенная выше глава - это весь процесс от идей, кода, а затем до визуализации. Вот адрес демонстрации. Если вам это нравится, вы можете щелкнуть запуск.
Демо -адрес: https://github.com/xiaolei123/okhttpcacheinterceptor
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.