凌空出现内存错误,奇怪的分配尝试

CQM*_*CQM 17 android out-of-memory android-volley diskcache

有时随机Volley在启动时崩溃我的应用程序,它在应用程序类中崩溃,用户无法再次打开应用程序,直到他们进入设置并清除应用程序数据

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)
Run Code Online (Sandbox Code Playgroud)

"diskbasedbache"尝试分配超过1 GB的内存,没有明显的原因

我怎么能让这件事不发生?这似乎是Volley的问题,或者可能是基于自定义磁盘的缓存的问题,但我没有立即看到(从堆栈跟踪)如何"清除"此缓存或执行条件检查或处理此异常

洞察力赞赏

Vin*_*ing 15

streamToBytes(),首先它会在缓存文件长度的字节新,贵缓存文件比应用程序的最大堆大小是否过大?

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());
}
Run Code Online (Sandbox Code Playgroud)

如果要清除缓存,可以DiskBasedCache在清除时间之后保留引用,使用ClearCacheRequest并传递该缓存实例:

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));
Run Code Online (Sandbox Code Playgroud)

这种方式将清除所有缓存,所以我建议你仔细使用它.当然,你可以做conditional check,只是迭代cacheDir文件,估计哪个太大然后删除它.

for (File cacheFile : cacheDir.listFiles()) {
    if (cacheFile.isFile() && cacheFile.length() > 10000000) cacheFile.delete();
}
Run Code Online (Sandbox Code Playgroud)

Volley并不是一个大数据缓存解决方案,它是常见的请求缓存,不会随时存储大数据.

-------------更新于2014-07-17 -------------

事实上,清除所有缓存是最后的方式,也是不明智的方式,我们应该在我们确定它时会抑制这些大量请求使用缓存,如果不确定的话?我们仍然可以确定响应数据大小是否大,然后调用setShouldCache(false)禁用它.

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);
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)