有时候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)
“diskbasedbache”试图分配超过1 GB的内存,没有明显的原因
我怎么能让这件事不发生?它似乎是Volley的一个问题,或者可能是基于自定义磁盘的缓存的问题,但我没有立即看到(从堆栈跟踪)如何“清除”此缓存或执行条件检查或处理此异常
洞察力赞赏
答案 0 :(得分: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());
}
如果要清除缓存,可以保留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));
这种方式会清除所有缓存,所以我建议你仔细使用它。当然,您可以执行conditional check
,只需迭代 cacheDir 文件,估算哪个文件太大然后删除它。
for (File cacheFile : cacheDir.listFiles()) {
if (cacheFile.isFile() && cacheFile.length() > 10000000) cacheFile.delete();
}
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);
...
}
}
答案 1 :(得分:3)
我遇到了同样的问题。
我们知道在缓存初始化时我们没有大小为GB的文件。在读取标题字符串时也会发生这种情况,标题字符串的长度绝不应该是GB。
所以看起来readLong读取的长度不正确。
我们有两个设置大致相同的设置,只有一个应用程序在启动时创建了两个独立的进程。主要的申请流程和一个&#39; SyncAdapter&#39;同步适配器模式后的进程。只有具有两个进程的应用程序才会崩溃。 这两个进程将独立初始化缓存。
但是,DiskBasedCache对两个进程使用相同的物理位置。我们最终得出结论,并发初始化导致同一文件的并发读取和写入,导致对size参数的错误读取。
我没有充分证明这是问题所在,但我打算在测试应用上进行验证。
在短期内,我们刚刚在streamToBytes中捕获了过大的字节分配,并抛出一个IOException,以便Volley捕获异常并删除该文件。 但是,为每个进程使用单独的磁盘缓存可能会更好。
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;
}
答案 2 :(得分:1)
一旦出现问题,它似乎会在每次后续初始化时重复出现,指向无效的缓存标头。
幸运的是,这个问题已在官方的Volley回购中得到修复:
查看android-volley镜像中的相关问题: