为什么我没有内存不足的例外?

时间:2015-05-01 14:30:58

标签: android bitmap out-of-memory android-drawable bitmapregiondecoder

我在可绘制文件夹中有高分辨率图像(2588 * 1603)。如果我使用下面的代码(1)为imageView设置它我没有得到OOM异常和按预期分配的图像:

public class MainActivity extends ActionBarActivity{


    private ImageView mImageView;

    int mImageHeight = 0;
    int mImageWidth  = 0;


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

      mImageView = (ImageView) findViewById(R.id.imageView);
      mImageView.setScaleType(ScaleType.FIT_CENTER);

      BitmapFactory.Options sizeOption = new BitmapFactory.Options();
      sizeOption.inJustDecodeBounds = true;
      BitmapFactory.decodeResource(getResources(), R.drawable.a, sizeOption);
      mImageHeight = sizeOption.outHeight;
      mImageWidth  = sizeOption.outWidth; 

      mImageView.post(new Runnable() {
          @Override
          public void run() {
              try {
                BitmapRegionDecoder bmpDecoder = BitmapRegionDecoder
                          .newInstance(getResources().openRawResource(R.drawable.a),true);
            Rect rect = new Rect(0,0,mImageWidth, mImageHeight);
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Bitmap.Config.ARGB_8888;
            options.inDensity = getResources().getDisplayMetrics().densityDpi;
            Bitmap bmp = bmpDecoder.decodeRegion(rect, options);

            mImageView.setImageBitmap(bmp);  

            } catch (NotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }   
          }
      });

    }
}

请注意,rect size与图片大小完全相同。

但如果我使用其他方法,例如2或3,我会得到OOM。

  2)  mImageView.setBackgroundResource(R.drawable.a);

  3) Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.a);
     mImageView.setImageBitmap(bmp);

1和2,3有什么区别?

(我知道如何解决OOM,我只是想知道其中的差异)

3 个答案:

答案 0 :(得分:2)

这是BitmapRegionDecoder#decodeRegion

的来源
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
    checkRecycled("decodeRegion called on recycled region decoder");
    if (rect.left < 0 || rect.top < 0 || rect.right > getWidth()
            || rect.bottom > getHeight())
        throw new IllegalArgumentException("rectangle is not inside the image");
    return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top,
            rect.right - rect.left, rect.bottom - rect.top, options);
}

如您所见,它只是调用本机方法。我不太了解C ++,看看该方法是否缩小了位图(根据您的inDensity标志)。

另外两种方法使用相同的本机方法(nativeDecodeAsset)来获取位图。

2号缓存 drawable,因此需要更多内存。
经过大量操作(检查位图是否已经预加载或兑现等),它调用本机方法来获取位图。然后,它缓存drawable并设置背景图像。

Number 3非常简单,它在几次操作后调用了一个本机方法。

<小时/> 结论:对我来说,很难说这里适用哪种情况,但它应该是这两种情况中的一种。

  1. 您的第一次尝试将位图缩小(inDensity标志),因此需要更少的内存。
  2. 所有三种方法都需要或多或少相同的内存量,数字2和3只需要更多一点。您的映像使用~16MB RAM,这是某些手机上的最大堆大小。数字1可能低于该限制,而其他两个略高于阈值。
  3. 我建议你调试这个问题。在你的Manifest中,设置android:largeHeap="true"以获得更多内存。然后,运行3种不同的尝试并记录堆大小和位图分配的字节。

    long maxMemory = Runtime.getRuntime().maxMemory();
    long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
    long freeMemory = maxMemory - usedMemory;
    long bitmapSize = bmp.getAllocationByteCount();
    

    这将为您提供更好的概述。

答案 1 :(得分:-1)

图片太多细节导致内存不足。

摘要:1使用缩放位图; 2,3加载完整的详细drawable(这会导致内存不足),然后调整大小并将其设置为imageview。

1

Bitmap bmp = bmpDecoder.decodeRegion(rect, options);

the constructor(InputStream is, boolean isShareable) use the stream,不会耗尽内存。

使用BitmapFactory.Options和BitmapRegionDecoder将缩小位图。

参考:BitmapRegionDecoder will draw its requested content into the Bitmap provided, clipping if the output content size (post scaling) is larger than the provided Bitmap. The provided Bitmap's width, height, and Bitmap.Config will not be changed

2,3

Drawable d = mContext.getDrawable(mResource);
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.a);

没有缩放选项,整个画面将加载到内存

抱歉英语。

也许帮助你。

答案 2 :(得分:-1)

  1. 由于此原因,您没有获得OOM异常

    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    
  2. 已经给出了here

        public Bitmap.Config inPreferredConfig
    

    在API级别1中添加

    如果这是非null,解码器将尝试解码到此内部配置。如果它为null,或者无法满足请求,解码器将尝试根据系统的屏幕深度和原始图像的特征选择最佳匹配配置,例如它是否具有每像素alpha(需要同样的配置)一样)。默认情况下,图像加载了ARGB_8888配置。