Для читаемости я разместил примеры кода, на которые ссылаются мои решения, и затем я перечислил объяснения своих решений в числовом списке.
Я боролся с этим какое-то время. Я много читал, задавал вопросы здесь и экспериментировал; но не придумали достойного решения. Мне нужно читать изображения различного размера из входных потоков и отображать их с таким высоким качеством, которое позволяют ограничить память. Ниже приведены варианты, которые я рассмотрел, ни один из которых не кажется мне большим. Любая помощь или ввод были бы весьма полезными.
public class NativeTest extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
double nativeUsage = Debug.getNativeHeapAllocatedSize();
Log.i("memory", nativeUsage+"");
}
}
double getAvailableMemory()
{
//current heap size
double heapSize = Runtime.getRuntime().totalMemory();
//amount available in heap
double heapRemaining = Runtime.getRuntime().freeMemory();
double nativeUsage = Debug.getNativeHeapAllocatedSize();
double memoryAvailable = Runtime.getRuntime().maxMemory() - (heapSize - heapRemaining) - nativeUsage;
return memoryAvailable;
}
Bitmap createImageTrialAndError(InputStream stream)
{
Bitmap image = null;
int dowsample = 1;
while(image == null)
{
try
{
Options opts = new Options();
opts.inSampleSize = downsample;
image = BitmapFactory.decodeStream(imgStream, null, opts);
}
catch (OutOfMemoryError ome)
{
downsample = downsample * 2;
Log.i("out of mem", "try using: " + downsample);
}
}
return image;
}
- Идеальное решение было бы, если Bitmap имел метод Bitmap.drawBitmap(входной поток...). Это позволило бы мне извлечь из входного потока без необходимости выделять память для битмапа. Увы, это не вариант.
- Масштабирование растрового изображения в соответствии с доступной памятью. Это включает в себя получение ширины и высоты битмапа, вычисление количества байтов, которое битмап требует в качестве
width * height * 4
, вычисления доступной памяти, а затемBitmapFactory.Options.inSampleSize
, так что битмап будет использовать меньше памяти, чем то, что доступно. Однако этот параметр выходит из строя, потому что я не смог найти удалённо надежный способ вычисления доступной памяти. Метод getAvailableMemory() выглядит так, как будто он должен работать: он вычисляет доступную память как максимальную память - память, используемую в куче java-памяти, используемой в нативной куче.
К сожалению, эта формула дает очень ненадежные результаты. В основном потому, чтоDebug.getNativeHeapAllocatedSize()
не является точным представлением использования памяти Bitmap. Одним из очевидных примеров его неточности является действие NativeTest, ниже. На моем планшете Samsung Galaxy выведено выражение журнала: 3759416.0. 3,75 мб собственного распределения для пустой активности, что явно не является надежным способом определения масштабирования растрового изображения. - Начните с коэффициента масштабирования единицы и попытайтесь инициализировать Bitmap; если инициализация завершилась неудачей из-за памяти, удвойте масштабный коэффициент и повторите попытку; и повторите этот процесс до достижения успеха. Это иллюстрируется
createBitmapTrialAndError()
. Это на самом деле удивительно эффективно и не очень медленно. Однако это очень нежелательно, потому что я использую SoftReferences в другом месте в своем приложении, а нехватка памяти заставляет собирать эти SoftReferences, что оказывает значительное влияние на производительность. Было бы гораздо более желательно знать правильный коэффициент масштабирования изначально, что позволило бы избежать ненужной коллекции этих SoftReferences. - Мое окончательное решение кажется немного сомнительным. В основном он объединяет 2 и 3, но не использует
Debug.getNativeAllocatedSize()
. Вместо этого я отслеживаю собственное распределение памяти Bitmap, отслеживая везде, где я выделяю растровые изображения и подсчитываю их использование памяти, а затем вычитаю использование памяти для любых растровых изображений, которые я перерабатываю. Я использую это значение вместо nativeUsage в getAvailableMemory(), чтобы рассчитать правильный коэффициент масштабирования для битмапов. И в случае исключения исключения из памяти при использовании этого метода я использую решение 3 как резервный способ вычисления приемлемого масштаба. Очевидная проблема с этим заключается в массивной отрывочности в попытке отслеживать использование моего собственного источника памяти, но для меня это кажется лучшим решением.