【问题标题】:Rotating images on android. Is there a better way?在android上旋转图像。有没有更好的办法?
【发布时间】:2011-11-28 11:03:53
【问题描述】:

我有一个应用程序可以为用户显示很多图像,我们已经看到很多带有OutOfMemoryError 异常的错误报告。

我们目前做的是这样的:

// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
    // Rotate it to show as a landscape
    Matrix m = image.getImageMatrix();
    m.postRotate(90);
    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
image.setImageBitmap(bmp);

这样做的明显问题是我们必须从内存中的图像重新创建位图并旋转矩阵,这对内存来说非常昂贵。

我的问题很简单:

有没有更好的方法来旋转图像而不会导致OutOfMemoryError

【问题讨论】:

  • 在哪一行抛出异常
  • @Dr.nik 所以,你建议我用动画旋转图像,即使它不应该是动画旋转?
  • @ingsaurabh 在Bitmap.createBitmap 上可能会超出 VM 的允许内存并引发错误。
  • 我已经更新了我的答案。它现在有了更好的解决方案。

标签: android out-of-memory android-imageview


【解决方案1】:

2种旋转大图的方法:

  1. 使用 JNI,例如 on this post

  2. 使用文件:这是一种非常慢的方式(取决于输入和设备,但仍然非常慢),它首先将解码后的旋转图像放入磁盘,而不是将其放入内存。

文件使用代码如下:

private void rotateCw90Degrees()
  {
  Bitmap bitmap=BitmapFactory.decodeResource(getResources(),INPUT_IMAGE_RES_ID);
  // 12 => 7531
  // 34 => 8642
  // 56 =>
  // 78 =>
  final int height=bitmap.getHeight();
  final int width=bitmap.getWidth();
  try
    {
    final DataOutputStream outputStream=new DataOutputStream(new BufferedOutputStream(openFileOutput(ROTATED_IMAGE_FILENAME,Context.MODE_PRIVATE)));
    for(int x=0;x<width;++x)
      for(int y=height-1;y>=0;--y)
        {
        final int pixel=bitmap.getPixel(x,y);
        outputStream.writeInt(pixel);
        }
    outputStream.flush();
    outputStream.close();
    bitmap.recycle();
    final int newWidth=height;
    final int newHeight=width;
    bitmap=Bitmap.createBitmap(newWidth,newHeight,bitmap.getConfig());
    final DataInputStream inputStream=new DataInputStream(new BufferedInputStream(openFileInput(ROTATED_IMAGE_FILENAME)));
    for(int y=0;y<newHeight;++y)
      for(int x=0;x<newWidth;++x)
        {
        final int pixel=inputStream.readInt();
        bitmap.setPixel(x,y,pixel);
        }
    inputStream.close();
    new File(getFilesDir(),ROTATED_IMAGE_FILENAME).delete();
    saveBitmapToFile(bitmap); //for checking the output
    }
  catch(final IOException e)
    {
    e.printStackTrace();
    }
  }

【讨论】:

  • @android_developer 喜欢你的文件解决方案,我认为值得花时间避免所有内存问题。
  • @PeterFile 我认为您应该在从最快(但最危险)到最慢(但最安全)的多个后备中执行此操作:首先尝试使用正常方式(使用堆)。如果失败(内存不足),请使用 JNI 解决方案。如果失败(内存不足),请使用存储解决方案。存储解决方案也可能失败(存储空间不足),因为创建的文件是解码的位图,而不是压缩的位图,但这可能很少发生。
  • @androiddeveloper 你的解决方案是否保持原始图像大小和质量?
  • @YoannHercouet 是的,因为我什至没有将其编码为压缩格式。
【解决方案2】:

你可以试试:

image.setImageBitmap(null);
// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
    // Rotate it to show as a landscape
    Matrix m = image.getImageMatrix();
    m.postRotate(90);
    bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
}
BitmapDrawable bd = new BitmapDrawable(mContext.getResources(), bmp);
bmp.recycle();
bmp = null;
setImageDrawable(bd);
bd = null;

【讨论】:

  • 正如问题中的评论所述,Bitmap.createBitmap 行中发生了错误。
【解决方案3】:

使用大量位图时,请确保在不需要时立即对它们调用 recycle()。此调用将立即释放与特定位图关联的内存。

如果您不需要旋转后的原始位图,请回收它。大致如下:

Bitmap result = bmp;

// Check if image is a landscape image
if (bmp.getWidth() > bmp.getHeight()) {
    // Rotate it to show as a landscape
    Matrix m = image.getImageMatrix();
    m.postRotate(90);
    result = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
    // rotating done, original not needed => recycle()
    bmp.recycle();
}

image.setImageBitmap(result);

【讨论】:

  • 很好,不知道。问题是我需要原始位图来旋转它,并且在我可以将他从内存中释放之前发生错误:/
  • 实际上我尝试了回收,它现在只显示黑屏而不是位图。我猜在内部它是一个指针,我真的不能乱用它。
  • 嗯。 Bitmap.createBitmap() 应该创建全新的位图,因此释放原始位图应该是安全的。实际上,我确实在自己的代码中大量使用了这种方法,并且效果很好。你确定你正在回收正确的位图,并且只有 将 createBitmap() 返回的旋转位图的引用分配给其他引用(如我的示例中)。如果您已经用完所有内存,它可能会在 createBitmap() 中引发错误。但是,如果每次使用 recycle() 正确释放它,下次调用应该没问题...
  • 当然,如果它在 first 调用时崩溃,那么我的回答将无济于事。这意味着当您使用 createBitmap() 进行分配时,内存已经几乎已满,并且无法容纳新对象。我的回答适用于您必须一个接一个地多次旋转许多位图的情况 - 然后回收肯定会有所帮助:)
  • 是的,我几乎尝试过你的方法,但它变成了空白。将新位图分配给不同的变量并回收原始位图。
猜你喜欢
  • 1970-01-01
  • 2020-03-01
  • 1970-01-01
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-16
  • 2014-09-15
相关资源
最近更新 更多