如何避免OOM从android资源加载大图像

时间:2018-09-06 02:00:33

标签: android resources out-of-memory

我已经开发了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 :(得分:3)

您可以按以下方式修改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];
}

某些类型的手机,尤其是三星手机,其内存泄漏设计不完善。

opt.inDensity = DisplayMetrics.DENSITY_DEFAULT 非常重要。

如果inDensity0,则BitmapFactory.decodeStream将填充与资源关联的密度。 more...

并且当您的活动(或页面)被破坏时,应将imageview的位图设置为null

答案 1 :(得分:3)

RGB_565配置中大小为750x1334的位图需要大约2MB的内存。其中3个可以存储在6 MB中,如果小心处理,则不应创建OutOfMemoryError

以下是您可以遵循的一些提示。

  • 在清单的应用程序标记中使用android:largeHeap="true"
  • FragmentStatePagerAdapter用于ViewPager
  • 别忘了在不再需要位图时立即对其进行回收。
  • 回收位图后使用Runtime.getRuntime().gc();
  • 如果要将图像存储在drawable中,请将其移动到drawable-nodpi
  • 使用Glide或Picasso库。

答案 2 :(得分:2)

尝试使用一些图片加载库,例如Picasso或Glide。这样可以简化很多事情。

但是对于您而言,我认为问题不在于解码图像,而是您要保留已在内存中创建的位图。确保始终只有一个位图在内存中。只需在Studio中打开Profiler,然后确认是否不存储对多个位图的引用即可。