Как избежать OOM для загрузки больших изображений с ресурса Android - PullRequest
0 голосов
/ 06 сентября 2018

Я разработал более 20 приложений для Android, у них есть учебное упражнение, состоящее из большого слайда изображений (например, 4 изображения размером 750 x 1334), которое будет показано пользователям при первом запуске.

Я больше не могу уменьшать размеры из-за качества изображения.

Следующий фрагмент моего кода;

public class GalleryImageActivity extends BaseActivity implements OnGestureListener, OnTouchListener {
ViewPager imagePager;
GalleryImageAdapter galleryImageAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.gallery_image);

    imagePager = (ViewPager) findViewById(R.id.image_pager);

    galleryImageAdapter = new GalleryImageAdapter(this);
    imagePager.setAdapter(galleryImageAdapter);
    imagePager.setOnTouchListener(this);
}
}


public class GalleryImageAdapter extends PagerAdapter {
public static int SCREEN_CNT = 4;
Bitmap[] bmp = new Bitmap[SCREEN_CNT];

@Override
public int getCount() {
    return SCREEN_CNT;
}

@Override
public Object instantiateItem(ViewGroup view, int position) {
    View view = inflater.inflate(R.layout.gallery_image_item, null);
    ImageView imv = (ImageView) view.findViewById(R.id.imgView);

    bmp[position] = readBitMap(context, R.drawable.tutorial_img_0 + position, position); 
    if (bmp[position] != null)
        imv.setImageBitmap(bmp[position]);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {

    if (bmp[position] != null && !bmp[position].isRecycled())
    {
        bmp[position].recycle();   
        System.gc();  
        bmp[position] = null;
    }
}

public Bitmap readBitMap(Context context, int resId, int position) { 
    BitmapFactory.Options opt = new BitmapFactory.Options();
    opt.inJustDecodeBounds = true;

    InputStream is = context.getResources().openRawResource(resId);
        BitmapFactory.decodeStream(is,null, opt);
    opt.inPreferredConfig = Bitmap.Config.RGB_565;
    opt.inPurgeable = true;
    opt.inInputShareable = true;
    opt.inJustDecodeBounds = false;

    try {
        is.close();
    } catch (IOException e) {                
        e.printStackTrace();
    }

    is = context.getResources().openRawResource(resId);
    Bitmap bitmap = BitmapFactory.decodeStream(is,null, opt);      

    try {
        is.close();
    } catch (IOException e) {                
        e.printStackTrace();
    }            

    return bitmap;
}
}

иногда это работает хорошо, но при повторении 5 ~ 6 раз BitmapFactory.decodeStream выбрасывает «Недостаточно памяти». Как я могу определить размер шкалы или решить эту проблему?

примеры изображений следующие; enter image description here

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Вы можете изменить функцию readBitMap следующим образом;

public Bitmap readBitMap(Context context, int resId, int position){ 
    if (bmp[position] == null || bmp[position].isRecycled())
    {
        BitmapFactory.Options opt = new BitmapFactory.Options();
        opt.inPreferredConfig = Bitmap.Config.RGB_565; 

        opt.inPurgeable = true;  
        opt.inInputShareable = true;        

        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        opt.inScaled = true;
        opt.inDensity = DisplayMetrics.DENSITY_DEFAULT; // !important

        InputStream is = context.getResources().openRawResource(resId);  
        try {               
            bmp[position] = BitmapFactory.decodeStream(is,null,opt);
        } catch (Exception e){
            bmp[position] = null;
        }           
    }

    return bmp[position];
}

Некоторые типы мобильных телефонов, особенно SAMSUNG, не предназначены для утечки памяти.

opt.inDensity = DisplayMetrics.DENSITY_DEFAULT очень важно.

Если inDensity равен 0, BitmapFactory.decodeStream заполнит плотность, связанную с ресурсом. подробнее ...

И вам следует установить растровое изображение для изображения null, когда ваша деятельность (или страница) будет уничтожена.

0 голосов
/ 06 сентября 2018

Растровое изображение размером 750x1334 в конфигурации RGB_565 требует около 2 МБ памяти. 3 из них могут храниться в 6 МБ, что не должно создавать OutOfMemoryError, если с ними обращаться осторожно.

Вот несколько советов, которым вы можете следовать.

  • Используйте android:largeHeap="true" в теге приложения вашего манифеста.
  • Используйте FragmentStatePagerAdapter для вашего ViewPager.
  • Не забудьте утилизировать растровые изображения, как только они больше не нужны.
  • Используйте Runtime.getRuntime().gc(); после перезапуска растровых изображений.
  • Если вы храните изображения в drawable, переместите их в drawable-nodpi.
  • Используйте библиотеку Glide или Picasso.
0 голосов
/ 06 сентября 2018

Попробуйте использовать какую-нибудь библиотеку для загрузки изображений, например, Picasso или Glide. Это многое упростит.

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

...