Android: вопрос о растровых изображениях, использовании памяти и масштабировании - PullRequest
13 голосов
/ 01 июня 2011

Для удобства чтения я разместил примеры кода, на которые сначала ссылаются мои решения, а затем перечислил объяснения моих решений в числовом списке.

Я уже давно борюсь с этим. Я много читал, задавал вопросы здесь и экспериментировал; но не придумали достойного решения. Мне нужно читать изображения разных размеров из входных потоков и отображать их с таким высоким качеством, насколько позволяют мои ограничения памяти. Ниже приведены варианты, которые я рассмотрел, ни один из которых не кажется мне замечательным. Любая помощь или вклад будет принята с благодарностью.

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

Ответы [ 3 ]

1 голос
/ 17 апреля 2015

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

Есть библиотека, которая должна делать именно это, проверьте это: https://github.com/davemorrissey/subsampling-scale-image-view

0 голосов
/ 18 июля 2013

Простой и понятный способ - использовать свойство "inJustDecodeBounds" в Options.Установите для этого свойства значение true для созданного вами объекта параметров, затем продолжите декодирование потока.

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

Позже сбросinJustDecodeBounds to false, теперь вы знаете коэффициент уменьшения, поэтому теперь можно генерировать растровое изображение требуемого размера.

Надеюсь, это поможет.

0 голосов
/ 24 февраля 2012

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

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

...