android out-of-memory android-volley diskcache

android - Volley fuera de error de memoria, intento de asignación extraño



out-of-memory android-volley (3)

En streamToBytes() , primero serán los nuevos bytes por la longitud del archivo de caché. ¿Su archivo de caché era demasiado grande que el tamaño máximo de almacenamiento de la aplicación?

private static byte[] streamToBytes(InputStream in, int length) throws IOException { byte[] bytes = new byte[length]; ... } public synchronized Entry get(String key) { CacheHeader entry = mEntries.get(key); File file = getFileForKey(key); byte[] data = streamToBytes(..., file.length()); }

Si desea borrar el caché, puede mantener la referencia de DiskBasedCache , después de que haya transcurrido el tiempo, utilice ClearCacheRequest y pase esa instancia de caché en:

File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); DiskBasedCache cache = new DiskBasedCache(cacheDir); RequestQueue queue = new RequestQueue(cache, network); queue.start(); // clear all volley caches. queue.add(new ClearCacheRequest(cache, null));

De esta forma se borrarán todos los cachés, así que sugiero que lo use con cuidado. Por supuesto, puede hacer una conditional check , simplemente iterar los archivos cacheDir , estimar que fue demasiado grande y luego eliminarlo.

for (File cacheFile : cacheDir.listFiles()) { if (cacheFile.isFile() && cacheFile.length() > 10000000) cacheFile.delete(); }

Volley no fue diseñado como una solución de caché de big data, es un caché de solicitud común, no almacenar datos de gran tamaño en cualquier momento.

------------- Actualización en 2014-07-17 -------------

De hecho, borrar todos los cachés es una forma final, tampoco es sabio, deberíamos suprimir este gran uso de caché de uso cuando estemos seguros, y si no estamos seguros? Todavía podemos determinar el tamaño de los datos de respuesta, ya sea grande o no, y luego llamar a setShouldCache(false) para deshabilitarlo.

public class TheRequest extends Request { @Override protected Response<String> parseNetworkResponse(NetworkResponse response) { // if response data was too large, disable caching is still time. if (response.data.length > 10000) setShouldCache(false); ... } }

Algunas veces, al azar, Volley bloquea mi aplicación al iniciarse, se bloquea en la clase de la aplicación y un usuario no podría abrir la aplicación nuevamente hasta que ingresen a la configuración y borren los datos de la aplicación.

java.lang.OutOfMemoryError at com.android.volley.toolbox.DiskBasedCache.streamToBytes(DiskBasedCache.java:316) at com.android.volley.toolbox.DiskBasedCache.readString(DiskBasedCache.java:526) at com.android.volley.toolbox.DiskBasedCache.readStringStringMap(DiskBasedCache.java:549) at com.android.volley.toolbox.DiskBasedCache$CacheHeader.readHeader(DiskBasedCache.java:392) at com.android.volley.toolbox.DiskBasedCache.initialize(DiskBasedCache.java:155) at com.android.volley.CacheDispatcher.run(CacheDispatcher.java:84)

El "diskbasedbache" intenta asignar más de 1 gigabyte de memoria, sin ninguna razón obvia

¿Cómo puedo hacer que esto no suceda? Parece ser un problema con Volley, o tal vez un problema con un caché basado en disco personalizado, pero no veo de inmediato (desde el seguimiento de la pila) cómo "borrar" este caché o hacer una comprobación condicional o manejar esta excepción

Conocimiento apreciado


Experimenté el mismo problema.

Sabíamos que no teníamos archivos de tamaño GB en la inicialización de la memoria caché. También ocurrió al leer cadenas de encabezado, que nunca deberían tener una longitud de GB.

Así que parecía que la longitud estaba siendo leída incorrectamente por readLong.

Teníamos dos aplicaciones con configuraciones aproximadamente idénticas, excepto que una aplicación tenía dos procesos independientes creados en el inicio. El proceso principal de la aplicación y un proceso ''SyncAdapter'' siguiendo el patrón del adaptador de sincronización. Solo la aplicación con dos procesos colapsaría. Estos dos procesos iniciarían independientemente el caché.

Sin embargo, DiskBasedCache usa la misma ubicación física para ambos procesos. Finalmente, llegamos a la conclusión de que las inicializaciones simultáneas resultaban en lecturas y escrituras concurrentes de los mismos archivos, lo que conducía a lecturas erróneas del parámetro de tamaño.

No tengo una prueba completa de que este sea el problema, pero estoy planeando trabajar en una aplicación de prueba para verificar.

A corto plazo, acabamos de capturar la asignación de bytes demasiado grande en streamToBytes y lanzamos una excepción IOException para que Volley detecte la excepción y simplemente elimine el archivo. Sin embargo, probablemente sería mejor usar un caché de disco separado para cada proceso.

private static byte[] streamToBytes(InputStream in, int length) throws IOException { byte[] bytes; // this try-catch is a change added by us to handle a possible multi-process issue when reading cache files try { bytes = new byte[length]; } catch (OutOfMemoryError e) { throw new IOException("Couldn''t allocate " + length + " bytes to stream. May have parsed the stream length incorrectly"); } int count; int pos = 0; while (pos < length && ((count = in.read(bytes, pos, length - pos)) != -1)) { pos += count; } if (pos != length) { throw new IOException("Expected " + length + " bytes, read " + pos + " bytes"); } return bytes; }