Для удобства чтения я разместил примеры кода, на которые сначала ссылаются мои решения, а затем перечислил объяснения моих решений в числовом списке.
Я уже давно борюсь с этим. Я много читал, задавал вопросы здесь и экспериментировал; но не придумали достойного решения. Мне нужно читать изображения разных размеров из входных потоков и отображать их с таким высоким качеством, насколько позволяют мои ограничения памяти. Ниже приведены варианты, которые я рассмотрел, ни один из которых не кажется мне замечательным. Любая помощь или вклад будет принята с благодарностью.
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 (inputtream ...). Это позволило бы мне рисовать из входного потока без необходимости выделять память для растрового изображения. Увы, это не вариант.
- Масштабировать растровое изображение в соответствии с доступной памятью. Это включает в себя получение ширины и высоты растрового изображения, вычисление количества байтов, требуемых для растрового изображения как
width * height * 4
, вычисление доступной памяти, а затем BitmapFactory.Options.inSampleSize
, так что растровое изображение будет использовать меньше памяти, чем доступно. Тем не менее, эта опция не работает, потому что я не смог найти удаленно надежный способ расчета доступной памяти. Метод getAvailableMemory (), приведенный ниже, похоже, должен работать: он вычисляет доступную память как максимальную память - память, используемую в куче Java - память, используемую в собственной куче.
К сожалению, эта формула дает очень ненадежные результаты. Главным образом потому, что Debug.getNativeHeapAllocatedSize()
не является точным представлением использования растровой памяти. Одним из очевидных примеров его неточности является NativeTest Activity, ниже. На моем планшете Samsung Galaxy выводится сообщение журнала: 3759416.0. 3,75 мегабайта собственного распределения для пустой операции, явно не надежный способ определения растрового масштабирования.
- Начните с коэффициента масштабирования, равного единице, и попытайтесь инициализировать растровое изображение; если инициализация не удалась из-за памяти, удвойте коэффициент масштабирования и повторите попытку; и повторяйте этот процесс до успешного завершения. Это иллюстрируется
createBitmapTrialAndError()
. Это на самом деле удивительно эффективно и не очень медленно. Тем не менее, это очень нежелательно, потому что я использую SoftReferences в другом месте моего приложения, и нехватка памяти вызывает сбор этих SoftReferences, что оказывает значительное влияние на производительность. Было бы гораздо более желательно знать правильный коэффициент масштабирования на начальном этапе, чтобы избежать ненужного сбора этих SoftReferences.
- Мое окончательное решение кажется немного сомнительным. В основном он объединяет 2 и 3, но не использует
Debug.getNativeAllocatedSize()
. Вместо этого я отслеживаю свое собственное распределение памяти для растровых изображений, отслеживая все, что я выделяю, и подсчитывая их использование памяти, а затем вычитая использование памяти любых растровых изображений, которые я перерабатываю. Я использую это значение вместо nativeUsage в getAvailableMemory (), чтобы вычислить правильный коэффициент масштабирования для растровых изображений. И в случае возникновения исключения Out Of Memory при использовании этого метода, я использую решение 3 в качестве запасного способа для вычисления приемлемого масштаба. Очевидная проблема с этим - массивная отрывочность попыток отследить использование моей собственной нативной памяти, но, мне кажется, это лучшее решение.