Prefácio: Algum tempo atrás, ao desenvolver aplicativos, geralmente não conseguia obter os dados obtidos da rede devido ao ambiente do dispositivo do usuário, portanto os resultados exibidos no aplicativo são sempre uma caixa em branco. Esta situação é extremamente ruim para a experiência do usuário. Portanto, pensei duro e decidi começar com o OKHTTP (porque a estrutura de solicitação de rede que usei no projeto é Okhttp) e escrevi um interceptor de cache de dados de rede.
Ok, então decidimos começar a escrever. Deixe -me primeiro falar sobre minhas idéias:
Idéias
Como estamos escrevendo sobre os interceptores de cache de dados de rede, que utilizam principalmente a poderosa função interceptora do OKHTTP, que dados devemos armazenar em cache ou em que circunstâncias devemos ativar o mecanismo de cache de dados?
Primeiro: as solicitações de postagem são suportadas, porque o funcionário forneceu um interceptador de cache, mas uma desvantagem é que elas só possam cache os dados solicitados pelo GET, mas não apoiam a postagem.
Segundo: quando a rede é normal, é buscar dados da rede. Se a rede for anormal, como o timeoutException IncnowHosception e outros problemas, precisamos armazenar em cache e recuperar dados.
Terceiro: se os dados obtidos no cache estiverem vazios, ainda precisamos deixar essa solicitação passar pelo processo normal restante.
Quarto: o chamador deve ter controle completo sobre o mecanismo de cache e pode decidir seletivamente se deve cache os dados de acordo com suas necessidades de negócios.
Quinto: deve ser simples de usar, que é o ponto mais importante.
OK, listamos cinco pontos acima, que são nossas idéias gerais. Vamos falar sobre a parte do código agora:
Artigo de código
Estrutura de armazenamento em cache: a estrutura de cache que eu uso aqui é o Disklrucache https://github.com/jakewharton/disklrucache Esta estrutura de cache pode ser armazenada localmente e foi aprovada pelo Google. Este também é o principal motivo para escolher essa estrutura. Eu também encapsule uma classe Cachemanager na estrutura de cache:
importar Android.content.Context; importar Android.content.pm.packageInfo; importar Android.content.pm.packageManager; importar com.xiaolei.okhttpcacheIntercept.ofile.log; import java.io.byTeraRaRaRaRaRaTream; java.io.ioException; importar java.io.OutputStream; importar java.io.unsupportEncodingException; importar java.security.Messagedigest; importar java.security.nosuchalgorithMexception; */public class Cachemanager {public static final string tag = "cachemanager"; // MAX CACHE TAMANHO 10MB PRIVADO ESTÁTICO PRIVADO LONG DISK_CACHE_SIZE = 1024 * 10; private estático final int disk_cache_index = 0; private estático final string cache_dir = "respostas"; Disklrucache privado mdisklrucache; Cachemanager estático volátil privado McAChemanager; public static Cachemanager getInstance (contexto de contexto) {if (mcachemanager == null) {sincronizado (cachemanager.class) {if (mcachemanager == null) {mcachemanager = new Cachemanager (contexto); }}} retornar McAChemanager; } private Cachemanager (contexto de contexto) {arquivo diskcachedir = getDiskcachedir (contexto, 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/*quantos arquivos correspondem a uma chave*/, diskcache_size); Log.d (tag, "mdisklrucache criado"); } catch (ioexception e) {e.printStackTrace (); }}} / *** Definir cache síncrono* / public void putcache (tecla String, String value) {if (mdisklrucache == null) return; OutputStream OS = NULL; tente {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 (); } finalmente {if (os! = null) {tente {os.close (); } catch (ioexception e) {e.printStackTrace (); }}}} / *** Definir assíncrono cache* / public void setCache (chave da string final, value final da string) {new Thread () {@Override public void run () {putcache (key, value); } }.começar(); } /*** síncrono getCache getCache (chave da string) {if (mdisklrucache == null) {return null; } FileInputStream fis = null; BytearrayOutputStream bos = null; tente {disklrucache.snapshot snapshot = mdisklrucache.get (Encryptmd5 (key)); if (snapshot! = null) {fis = (fileInputStream) snapshot.getInputStream (disk_cache_index); bos = novo bytearrayOutputStream (); byte [] buf = novo byte [1024]; int len; while ((len = fis.read (buf))! = -1) {bos.write (buf, 0, len); } byte [] data = bos.tobytearray (); retornar nova string (dados); }} catch (ioexception e) {e.printStackTrace (); } finalmente {if (fis! = null) {tente {fis.close (); } catch (ioexception e) {e.printStackTrace (); }} if (bos! = null) {tente {bos.close (); } catch (ioexception e) {e.printStackTrace (); }}} retornar nulo; } / *** assíncrono getcache* / public void getCache (chave de string final, retorno de chamada do Cachecallback final) {new Thread () {@Override public void run () {string cache = getCache (key); callback.ongetCache (cache); } }.começar(); } / *** Remova o cache* / public boolean removecache (chave da string) {if (mdisklrucache! = Null) {try {return mdisklrucache.remove (Encryptmd5 (key)); } catch (ioexception e) {e.printStackTrace (); }} retornar false; } / *** Obtenha diretório de cache* / arquivo privado getDiskcachedir (contexto de contexto, string unikeName) {string cachepath = context.getCachedir (). GetPath (); retornar novo arquivo (Cachepath + File.separator + Uniquename); } / *** codificação md5 da string* / public static string ecryptmd5 (string string) {try {byte [] hash = Messagedigest.getInstance ("md5"). Digest (string.getBytes ("utf-8")); StringBuilder Hex = new StringBuilder (hash.length * 2); para (byte b: hash) {if ((b & 0xff) <0x10) {hex.append ("0"); } hex.append (integer.tohexstring (b & 0xff)); } retornar hex.toString (); } catch (nosuchalgorithMexception | UnsupportEdEncodingException e) {e.printStackTrace (); } retornar string; } / *** Obtenha o número da versão do aplicativo* / private int getAppVersion (contexto de contexto) {packAGeManager pm = context.getpackageManager (); tente {packageInfo pi = pm.getpackageInfo (context.getpackagename (), 0); retornar pi == null? 0: pi.versionCode; } catch (packageManager.namenotfoundException e) {e.printStackTrace (); } retornar 0; }} Interceptador de CacheInterceptor: usa o mecanismo interceptador interceptador da OKHTTP para julgar de maneira inteligente cenários de cache e condições de rede e lidar com diferentes cenários.
importar Android.content.Context; importar com.xiaolei.okhttpcacheIntercept.catch.cachemanager; importar com.xiaolei.okhttpcacheIntercept.Log.log OkhTpT3.IninterigMORTO.IOEXCIPCECCECTION; importante okhttp3....LOG OkhTtpTpTPTPTPTPTPTPTTPor; okhttp3.request; importar okhttp3.Response; importar okhttp3.ResponseBody;/*** CAST CACHE de string* criado por xiaolei em 2017/12/9. */public class CacheIntercept implementa interceptores {contexto privado contexto; public void setContext (contexto de contexto) {this.Context = context; } public cacheIntercept (contexto de contexto) {this.Context = context; } @Override Respoção pública Intercept (cadeia) lança IoException {request request = Chain.Request (); String cachehead = request.header ("cache"); String cache_control = request.Header ("Cache-Control"); if ("true" .equals (cachehead) || // significa cache (cache_control! = null &&! cache_control.isEmpty ())) // também suporta cabeçalhos de cache para protocolos do lado da web {long Oldnow = System.curntiMillis (); String url = request.url (). Url (). Tostring (); String ResponsEST = NULL; String reqbodystr = getPostParams (solicitação); tente {Response Response = Chain.proeced (request); if (Response.issuccessful ()) // O processamento de cache é realizado somente após a solicitação de rede retornar com sucesso. Caso contrário, 404 é armazenado no cache. Não é uma piada? {Responsebody ResponsBody = Response.body (); if (ResponseBody! = null) {Responsest = ResponseBody.String (); if (responstr == null) {Responsest = ""; } Cachemanager.getInstance (context) .setcache (cachemanager.encryptmd5 (url + reqbodystr), ResponsEst); // armazenar o cache, use o parâmetro Link + para o md5 codificação: "Url": "HttProtrofit) } retornar getOnlineResponse (resposta, responseST); } else {return Chain.proeced (request); }} Catch (Exceção e) {resposta Response = getCacheResponse (solicitação, OldNow); // Uma exceção ocorreu, comecei a armazenar em cache aqui, mas pode não ser armazenado em cache, então preciso jogá -lo na próxima rodada de processamento por um longo tempo se (resposta == null) {retornar a cadeia.proeced (request); // cair para a próxima rodada de processamento} else {return Response; }}} else {return Chain.proeced (request); }} Resposta privada getCacheResponse (solicitação de solicitação, long OldNow) {log.i ("httpretrofit", "-> Tente obter cache ---------"); String url = request.url (). Url (). Tostring (); String params = getPostParams (solicitação); String cachest = cachemanager.getInstance (contexto) .getCache (cachemanager.encryptmd5 (url + params)); // obtenha cache, use o link + parâmetros para codificação md5 para chave para obter se (cachests == null) {log.i (", httProfTrit"; retornar nulo; } Resposta resposta = new Response.builder () .code (200) .body (respostabody.create (null, cachest) .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", cachest + ""); resposta de retorno; } Resposta privada getOnlineResponse (resposta da resposta, corpo da string) {ResponseBody ResponsBody = Response.body (); retornar nova resposta.builder () .code (Response.code ()) .body (Responsbody.Create (ResponseBody == NULL? } /*** Entre no modo de postagem. Parâmetros enviados para o servidor * * @param request * @return */ private string getPostParams (solicitação de solicitação) {string reqbodystr = ""; Método da String = request.Method (); if ("post" .equals (método)) // Se for post, analise cada parâmetro o máximo possível {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 ()); }} retornar reqbodystr; }}O exposto acima é a idéia principal e o principal código de implementação. Vamos falar sobre o método de uso agora
Como usar:
gradle usa:
Compile 'com.xiaolei: okhttpcacheInterceptor: 1.0.0'
Como acaba de ser enviado ao JCenter, pode não ser capaz de retirá -lo (ainda não foi revisado). Um leitor ansioso pode adicionar meu link Maven aos repositórios em seu projeto: Build.gradle:
allProjects {repositórios {maven {url 'https://dl.bintray.com/kavipyouxiang/Maven'}}}Criamos um novo projeto, e a captura de tela do projeto é a seguinte:
Captura de tela do projeto
A demonstração é muito simples, uma página principal, um feijão, uma adaptação, uma interface de solicitação de rede
Observe que, por ser uma rede, cache e relacionado, não há dúvida de que precisamos adicionar permissões de solicitação de rede e arquivar permissões de leitura e gravação no manifesto:
<usa-permission Android: name = "android.permission.internet" /> <usa-permission Android: name = "Android.permission.read_external_storage" /> <usa-permission Android: name = "Android.permission.write_external_storage" />
Ao usá -lo, você só precisa adicionar um interceptador ao seu OkhttpClient:
cliente = new okhttpclient.builder () .addinterceptor (novo cacheIntercept (contexto)) // Adicione interceptor de cache, adicione o suporte do cache.retryonconnectionFailure (true) // FAILENTE RECONNECT.ConnectTimeout (30, TimeUnit.Seconds) // A unidade de tempo limite da rede
Se você deseja armazenar em cache dados de qual interface, por muito tempo, adicione um cabeçalho de solicitação para sua interface de rede. A classe CacheHeaders.java contém todas as situações. Geralmente, apenas CacheHeaders.Normal é necessário.
interface pública net {@headers (cacheheaders.normal) // Aqui está a chave @FormURLEncoded @Post ("geocoding") Public Call <Databean> getIndex (@field ("a") string a);}Código de negócios:
Net net = retrofitBase.getReTrofit (). Create (net.class); Ligue para <dAbean> ligue = net.getIndex ("Suzhou City"); Call.enQueue (novo retorno de chamada <DABean> () {@Override public void onResponse (ligue para <daBean> ligue, resposta <dABean> resposta) {DATABEAN DATA = Response.body (); Date = new);); OnFailure (ligue para <Databean> Ligue, Throwable T) {TextView.Settext ("Solicitação falhou!");Se nossa solicitação de rede for bem -sucedida, produziremos texto na interface e adicionaremos a hora atual. Se a rede falhar, a saída de uma solicitação falhará.
Provavelmente este é o código, o código detalhado será publicado no final do artigo
Veja o efeito: demonstração
Aqui, demonstramos que da rede normal para a rede anormal e depois retornamos a situações normais.
Final
O capítulo acima é o processo inteiro, desde idéias, código e, em seguida, a renderizações. Aqui está o endereço da demonstração. Se você gosta, pode clicar em Iniciar.
Endereço da demonstração: https://github.com/xiaolei123/okhttpcacheIntercept
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.