Подтвердить что ты не робот

Ошибка воли за пределами памяти, странная попытка размещения

Иногда случайным образом волейбол сбрасывает мое приложение при запуске, он падает в классе приложения, и пользователь не сможет снова открыть приложение, пока не войдет в настройки и не очистит данные приложения.

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 гигабайта памяти, без видимой причины

как я могу сделать это не так? Похоже, что проблема связана с Volley, или, может быть, с кэшем на основе диска, но я не вижу сразу (из трассировки стека), как "очистить" этот кеш или выполнить условную проверку или обработать это исключение.

Проницательность оценена

4b9b3361

Ответ 1

В 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);
        ...
    }
}

Ответ 2

У меня возникла одна и та же проблема.

Мы знали, что при инициализации кеша у нас не было файлов размером в GB. Это также произошло при чтении строк заголовков, длина которых никогда не должна быть длинной.

Таким образом, это выглядело так, что длина читалась неправильно с помощью readLong.

У нас было два приложения с примерно одинаковыми настройками, за исключением того, что у одного приложения было два независимых процесса, созданных при запуске. Основной процесс приложения и процесс "SyncAdapter" после шаблона адаптера синхронизации. Только приложение с двумя процессами потерпит крах. Эти два процесса будут независимо инициализировать кеш.

Однако DiskBasedCache использует одно и то же физическое местоположение для обоих процессов. В заключение мы пришли к выводу, что параллельные инициализации приводят к одновременному чтению и записи одних и тех же файлов, что приводит к плохим считываниям параметра размера.

У меня нет полного доказательства того, что это проблема, но я планирую проверить тестовое приложение.

В краткосрочной перспективе мы просто поймали слишком большое распределение байтов в 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;
}

Ответ 3

Как только проблема возникает, она, кажется, повторяется при каждой последующей инициализации, указывая на недопустимый кешированный заголовок.

К счастью, эта проблема была исправлена ​​в официальном репетиторе Volley:

См. связанные проблемы в зеркале Android-волейбола: