13

我在可绘制文件夹中有一个高分辨率图像(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();
            }   
          }
      });

    }
}

请注意,矩形大小与图像大小完全相同。

但是如果我使用其他方法,例如 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,我只是想知道区别)

4

4 回答 4

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 号缓存可绘制对象,因此需要更多内存。
经过大量操作(检查位图是否已经预加载或兑现等)后,它会调用本机方法来获取位图。然后,它缓存可绘制对象并设置背景图像。

数字 3 非常简单,它在几次操作后调用本机方法。


结论:对我来说,很难说哪种情况适用于此,但应该是这两种情况之一。

  1. 您的第一次尝试会缩小位图(inDensity标志),因此需要更少的内存。
  2. 所有三种方法都需要或多或少相同数量的内存,数字 2 和 3 只是多一点。您的图像使用 ~16MB RAM,这是某些手机上的最大堆大小。数字 1 可能低于该限制,而其他两个则略高于阈值。

我建议你调试这个问题。在您的清单中,设置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();

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

于 2015-05-06T10:58:08.880 回答
0

好的,归根结底,1 和 2,3 之间的唯一区别是 1 不支持九个补丁和可清除对象。因此,在解码过程中为 NinePatchPeeker 分配的一些额外内存很可能是触发 OOM 的原因 2 和 3(因为它们使用相同的后端)。如果为 1,则不分配。

除此之外,我没有看到任何其他选择。如果您查看图像数据解码,则由于图像索引,平铺解码使用稍微更多的内存,因此如果是这种情况,情况将相反:1 会抛出 OOM,而 2,3 不会。

于 2015-05-10T22:01:51.327 回答
-1

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

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

1

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

构造函数(InputStream is, boolean isShareable)使用流,不会耗尽内存。

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

参考:BitmapRegionDecoder 会将其请求的内容绘制到提供的 Bitmap 中,如果输出内容大小(后缩放)大于提供的 Bitmap,则进行剪切。提供的 Bitmap 的宽度、高度和 Bitmap.Config 不会改变

2,3

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

没有缩放选项,整张图片会加载到内存中

对不起英语。

也许能帮到你。

于 2015-05-04T08:56:24.857 回答
-1
  1. 因此,您没有收到 OOM 异常

    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    

这里已经给出

    public Bitmap.Config inPreferredConfig

在 API 级别 1 中添加

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

于 2015-05-07T18:24:08.137 回答