studio - ¿Cómo implementar el almacenamiento en caché en la aplicación de Android para los resultados de la API REST?
progressive web apps tutorial español (3)
Mi aplicación de Android obtiene sus datos mediante la API REST. Quiero tener el caché del lado del cliente implementado. ¿Tenemos alguna clase incorporada para esto?
Si no, ¿son estos códigos que puedo reutilizar? Recuerdo haber encontrado tal código en algún momento atrás. Sin embargo no puedo encontrarlo.
Si nada más funciona, escribiré el mío. La siguiente es la estructura básica
public class MyCacheManager {
static Map<String, Object> mycache;
public static Object getData(String cacheid) {
return mycache.get(cacheid);
}
public static void putData(String cacheid, Object obj, int time) {
mycache.put(cacheid, obj);
}
}
¿Cómo habilito el tiempo para objetos en caché? También, ¿cuál es la mejor manera de serializar? la memoria caché debe estar intacta incluso si la aplicación se cierra y se vuelve a abrir más tarde (si el tiempo no ha expirado).
Gracias ajay
Ahora, la impresionante biblioteca Volley se lanzó en Google I / O 2013, lo que ayuda a mejorar todos los problemas de llamar a la API REST:
Volley es una biblioteca , es una biblioteca llamada Volley del equipo de desarrollo de Android. Eso hace que las redes para aplicaciones de Android sean más fáciles y, lo que es más importante, más rápidas. Administra el procesamiento y el almacenamiento en caché de las solicitudes de red y ahorra un tiempo valioso a los desarrolladores al escribir una y otra vez el mismo código de llamada / caché de la red. Y un beneficio más de tener menos código es una menor cantidad de errores y eso es lo que todos los desarrolladores quieren y buscan.
Ejemplo para volley: technotalkative
Primero verifique que el dispositivo esté conectado a internet o no.
public class Reachability {
private final ConnectivityManager mConnectivityManager;
public Reachability(Context context) {
mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public boolean isConnected() {
NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnectedOrConnecting();
}}
Si el dispositivo está conectado desde Internet, obtenga los datos de la API y la memoria caché; de lo contrario, obtendrá los datos de la memoria caché.
public class CacheManager {
Cache<String, String> mCache;
private DiskLruCache mDiskLruCache;
private final Context mContext;
public CacheManager(Context context) throws IOException {
mContext = context;
setUp();
mCache = DiskCache.getInstanceUsingDoubleLocking(mDiskLruCache);
}
public void setUp() throws IOException {
File cacheInFiles = mContext.getFilesDir();
int version = BuildConfig.VERSION_CODE;
int KB = 1024;
int MB = 1024 * KB;
int cacheSize = 400 * MB;
mDiskLruCache = DiskLruCache.open(cacheInFiles, version, 1, cacheSize);
}
public Cache<String, String> getCache() {
return mCache;
}
public static class DiskCache implements Cache<String, String> {
private static DiskLruCache mDiskLruCache;
private static DiskCache instance = null;
public static DiskCache getInstanceUsingDoubleLocking(DiskLruCache diskLruCache){
mDiskLruCache = diskLruCache;
if(instance == null){
synchronized (DiskCache.class) {
if(instance == null){
instance = new DiskCache();
}
}
}
return instance;
}
@Override
public synchronized void put(String key, String value) {
try {
if (mDiskLruCache != null) {
DiskLruCache.Editor edit = mDiskLruCache.edit(getMd5Hash(key));
if (edit != null) {
edit.set(0, value);
edit.commit();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public synchronized String get(String key) {
try {
if (mDiskLruCache != null) {
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(getMd5Hash(key));
if (snapshot == null) {
// if there is a cache miss simply return null;
return null;
}
return snapshot.getString(0);
}
} catch (IOException e) {
e.printStackTrace();
}
// in case of error in reading return null;
return null;
}
@Override
public String remove(String key) {
// TODO: implement
return null;
}
@Override
public void clear() {
// TODO: implement
}
}
public static String getMd5Hash(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] messageDigest = md.digest(input.getBytes());
BigInteger number = new BigInteger(1, messageDigest);
String md5 = number.toString(16);
while (md5.length() < 32)
md5 = "0" + md5;
return md5;
} catch (NoSuchAlgorithmException e) {
Log.e("MD5", e.getLocalizedMessage());
return null;
}
}}
Cree la clase CacheInterceptor para almacenar en caché la respuesta de la red y manejar los errores
public class CacheInterceptor implements Interceptor{
private final CacheManager mCacheManager;
private final Reachability mReachability;
public CacheInterceptor(CacheManager cacheManager, Reachability reachability) {
mCacheManager = cacheManager;
mReachability = reachability;
}
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
String key = request.url().toString();
Response response;
if (mReachability.isConnected()) {
try {
response = chain.proceed(request);
Response newResponse = response.newBuilder().build();
if (response.isSuccessful()) {
if (response.code() == 204) {
return response;
}
// save to cache this success model.
mCacheManager.getCache().put(key, newResponse.body().string());
// now we know that we definitely have a cache hit.
return getCachedResponse(key, request);
}else if (response.code() >= 500) { // accommodate all server errors
// check if there is a cache hit or miss.
if (isCacheHit(key)) {
// if data is in cache, the return the data from cache.
return getCachedResponse(key, request);
}else {
// if it''s a miss, we can''t do much but return the server state.
return response;
}
}else { // if there is any client side error
// forward the response as it is to the business layers to handle.
return response;
}
} catch (ConnectException | UnknownHostException e) {
// Internet connection exception.
e.printStackTrace();
}
}
// if somehow there is an internet connection error
// check if the data is already cached.
if (isCacheHit(key)) {
return getCachedResponse(key, request);
}else {
// if the data is not in the cache we''ll throw an internet connection error.
throw new UnknownHostException();
}
}
private Response getCachedResponse(String url, Request request) {
String cachedData = mCacheManager.getCache().get(url);
return new Response.Builder().code(200)
.body(ResponseBody.create(MediaType.parse("application/json"), cachedData))
.request(request)
.protocol(Protocol.HTTP_1_1)
.build();
}
public boolean isCacheHit(String key) {
return mCacheManager.getCache().get(key) != null;
}}
Ahora agregue este interceptor en OkHttpClient mientras crea el servicio usando Retrofit.
public final class ServiceManager {
private static ServiceManager mServiceManager;
public static ServiceManager get() {
if (mServiceManager == null) {
mServiceManager = new ServiceManager();
}
return mServiceManager;
}
public <T> T createService(Class<T> clazz, CacheManager cacheManager, Reachability reachability) {
return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT), cacheManager, reachability);
}
private <T> T createService(Class<T> clazz, HttpUrl parse, CacheManager cacheManager, Reachability reachability) {
Retrofit retrofit = getRetrofit(parse, cacheManager, reachability);
return retrofit.create(clazz);
}
public <T> T createService(Class<T> clazz) {
return createService(clazz, HttpUrl.parse(ServiceApiEndpoint.SERVICE_ENDPOINT));
}
private <T> T createService(Class<T> clazz, HttpUrl parse) {
Retrofit retrofit = getRetrofit(parse);
return retrofit.create(clazz);
}
private <T> T createService(Class<T> clazz, Retrofit retrofit) {
return retrofit.create(clazz);
}
private Retrofit getRetrofit(HttpUrl httpUrl, CacheManager cacheManager, Reachability reachability) {
return new Retrofit.Builder()
.baseUrl(httpUrl)
.client(createClient(cacheManager, reachability))
.addConverterFactory(getConverterFactory())
.build();
}
private OkHttpClient createClient(CacheManager cacheManager, Reachability reachability) {
return new OkHttpClient.Builder().addInterceptor(new CacheInterceptor(cacheManager, reachability)).build();
}
private Retrofit getRetrofit(HttpUrl parse) {
return new Retrofit.Builder()
.baseUrl(parse)
.client(createClient())
.addConverterFactory(getConverterFactory()).build();
}
private Retrofit getPlainRetrofit(HttpUrl httpUrl) {
return new Retrofit.Builder()
.baseUrl(httpUrl)
.client(new OkHttpClient.Builder().build())
.addConverterFactory(getConverterFactory())
.build();
}
private Converter.Factory getConverterFactory() {
return GsonConverterFactory.create();
}
private OkHttpClient createClient() {
return new OkHttpClient.Builder().build();
}}
Interfaz de caché
public interface Cache<K, V> {
void put(K key, V value);
V get(K key);
V remove(K key);
void clear();}
Una de las mejores maneras es usar las bibliotecas encendidas de Matthias Käppler para realizar solicitudes http que almacenen en caché las respuestas en la memoria (referencia débil) y en el archivo. Es realmente configurable para hacer uno u otro o ambos.
La biblioteca se encuentra aquí: https://github.com/mttkay/ignition con los ejemplos ubicados aquí: https://github.com/mttkay/ignition/wiki/Sample-applications
Personalmente, me encanta esta lib de cuando se llamaba Droidfu.
Espero que esto te ayude tanto como a mí, Ajay!