Android: как работает Bitmap recycle()?
Допустим, я загрузил изображение в растровом объекте, как
Bitmap myBitmap = BitmapFactory.decodeFile(myFile);
Теперь, что произойдет, если я загружу другое растровое изображение, как
myBitmap = BitmapFactory.decodeFile(myFile2);
Что происходит с первым myBitmap? Получает ли он сбор мусора или мне приходится вручную собирать мусор перед загрузкой другого растрового изображения, например. myBitmap.recycle()
?
Кроме того, есть ли лучший способ загружать большие изображения и отображать их один за другим во время переработки?
5 ответов
Первое растровое изображение не GC'ed, когда вы декодируете второе. GC сделает это позже, когда решит. Если вы хотите освободить память как можно скорее, вы должны вызвать recycle() непосредственно перед декодированием второго растрового изображения.
Если вы хотите загрузить действительно большое изображение, вам следует пересмотреть его. Вот пример странной проблемы с памятью при загрузке изображения в растровое изображение.
Я думаю, что проблема заключается в следующем: в версиях Android, предшествующих Honeycomb, фактические необработанные растровые данные хранятся не в памяти виртуальной машины, а в собственной памяти. Эта родная память освобождается, когда соответствующая Java Bitmap
объект GC'd.
Однако, когда у вас заканчивается собственная память, dalvik GC не запускается, поэтому возможно, что ваше приложение использует очень мало java-памяти, поэтому dalvik GC никогда не вызывается, хотя для растровых изображений используются тонны собственной памяти. что в конечном итоге вызывает ошибку OOM.
По крайней мере, это мое предположение. К счастью, в Honeycomb и более поздних версиях все растровые данные хранятся на виртуальной машине, поэтому вам не нужно использовать recycle()
совсем. Но для миллионов пользователей 2.3 (фрагментация сотрясает кулак), вы должны использовать recycle()
везде, где это возможно (огромные хлопоты). Или же вы можете вместо этого вызывать GC.
Вам нужно будет вызвать myBitmap.recycle() перед загрузкой следующего изображения.
В зависимости от источника вашего myFile (например, если это то, что вы не можете контролировать по размеру оригинала), при загрузке изображения вместо простой повторной дискретизации произвольного числа, вы должны масштабировать изображение до размера дисплея.
if (myBitmap != null) {
myBitmap.recycle();
myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
original.recycle();
original = null;
Я кэширую displayWidth & displayHeight в статическом виде, который я инициализировал в начале своей Деятельности.
Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
После того, как растровое изображение было загружено в память, фактически оно было создано из двух частей данных. Первая часть включает в себя некоторую информацию о растровом изображении, другая часть включает в себя информацию о пикселях растрового изображения (оно размечается байтовым массивом). Первая часть существует в используемой памяти Java, вторая часть существует в используемой памяти C++. Он может использовать память друг друга напрямую. Bitmap.recycle() используется для освобождения памяти C++. Если вы только это сделаете, GC соберет часть java, и память C всегда используется.
Тимммм был прав.
в соответствии с: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
Кроме того, до Android 3.0 (API Level 11) данные поддержки растрового изображения хранились в собственной памяти, которая не высвобождалась предсказуемым образом, что потенциально приводило к тому, что приложение кратковременно превышало пределы памяти и происходило сбой.