【问题标题】:java.lang.OutOfMemory with large high-resolution imagesjava.lang.OutOfMemory 具有大的高分辨率图像
【发布时间】:2012-12-23 22:03:50
【问题描述】:

我正在开发一个 Android 应用,该应用在其多个活动中使用多个大图像。每个图像大约为 1280x800,我为每个 Activity 加载大约 2-4 个这些图像。我意识到这些图像在分配给设备上每个单独应用程序的内存方面非常大,但是我怎样才能以原始分辨率显示它们而不会遇到 java.lang.OutOfMemory 错误?我需要这些图像在屏幕上以全尺寸显示(当屏幕小于图像时,xml 会自动进行缩放)。我看到了几种解决方案,涉及将图像缩小为缩略图并将其存储到内存中,但这不会导致图像失去其原始大小/分辨率吗?感谢您的帮助!

【问题讨论】:

    标签: android memory bitmap imageview allocation


    【解决方案1】:

    您可以做一些事情来解决这个问题。

    首先想到的是 1280x800(可能)是您的整个屏幕,因此您一次只需要显示一个。当你这样做时,不要把其他人留在记忆中。

    每像素 4 字节的 1280x800 图像仍然只有 4MB,而且平板电脑现在似乎都提供 48MB 的堆。如果需要,您应该能够在内存中保存一些。如果您的内存不足,则可能会泄漏。如果您在 DDMS 中观看,您的内存使用量是否会随着您更改活动而继续增长?

    常见的泄漏源是位图本身。完成后请务必致电Bitmap#recycle

    如果确实是这样,并且您无法适应提供的堆空间,您也可以尝试将android:largeHeap="true" 添加到清单中的应用程序标记中。这将要求系统为您提供更多堆空间 - 在某些设备上高达 256MB。不过,这应该是最后的手段,因为它会因设备而异,并且在某些设备上会被完全忽略(想到最初的 Kindle Fire)。

    您可以通过Runtime.getRuntime().maxMemory(); 查看您拥有的总堆空间。有关更详细的说明,请参阅this answer。查看您使用了多少会比较棘手,但如果您想解决这个问题,请参阅here 的描述。

    最后,加载图像可能比在 xml 中指定图像更好。请务必阅读此developer guide page。即使您必须将它们保留在 xml 中,我也看到通过将图像资产拆分到 drawable-hdpidrawable-mdpi 等目录而不是仅仅将它们转储到 drawable 中,内存使用率得到了显着改善。

    【讨论】:

    • 感谢您的回复,实际上我通过使用 Photoshop 重新保存我的图像克服了这个问题(由于某种原因,原始图像搞乱了 Android 中的位图创建/管理过程)。此外,每当我更改活动时,我都会使用 bitmap.recycle() 行来释放内存。不幸的是,每当我现在(使用后退按钮)返回该活动时,我都会收到“尝试使用回收的位图”错误。我知道我需要在 onResume() 方法中重置位图,但我该怎么做呢?再次感谢!
    • 匹配您的创作/回收。如果您在onStart 中创建位图,请在onStop 中回收。如果您在onResume 中创建它们,请在onPause 中回收。如果它们是通过 xml 加载的,请不要费心回收它们;在这种情况下,它由框架处理(但我相信它们会一直保存在内存中,直到onDestroy,这可能只要您的应用程序还活着)。
    • 我理解问题的理论部分,但我将使用什么代码来实际完成解决方案?我想我不知道如何在调用 recycle() 后获取每个 ImageView 的相应位图并将它们链接在一起。你能帮我解决那个代码吗?感谢您的帮助...这是我在启动应用程序之前需要克服的最后一个障碍,如果您能帮助我处理这段代码,那就太好了。 :)
    • 基于您问题的这一部分:“(当屏幕小于图像时,xml 会自动进行缩放)”,我假设您只是在 xml 中引用您的位图,例如android:src="@drawable/..."。如果是这样,您不需要回收它们。如果框架加载它们,框架就会释放它们。如果您在代码中加载它们,请发布该代码,我会看看。您的第一条评论表明您毕竟没有内存问题,但如果是,您应该按照 Ramesh 的回答开始寻找泄漏。
    • 是的,我正在使用 android:src="@drawable/..." 符号加载图像。所以你的意思是当活动暂停(甚至销毁)时,位图会自动释放?当我在 Java 代码中分配位图/图像时,我只需要手动进行回收,对吗?此外,当我调用新活动时,我在“setContentView(R.layout.xxx)”处收到 OOM 错误。基本上,我在 Activity A 中,它有多个图像(全部为 200x200 和 1 1280x800),当调用 Activity B 时,它无法设置布局内容。谢谢!
    【解决方案2】:

    This article 很好地描述了如何创建堆转储并使用 Eclipse MAT 对其进行分析。这将帮助您快速找到最可能的内存泄漏嫌疑人。

    我再次指出 this great link 我从另一个 SO Question 中找到,该问题有关于如何正确解决问题的教程。

    【讨论】:

      【解决方案3】:

      使用BitmapFactory或相关方法加载图片前需要对图片进行缩放。

      public static int calculateInSampleSize(
                  BitmapFactory.Options options, int reqWidth, int reqHeight) {
          // Raw height and width of image
          final int height = options.outHeight;
          final int width = options.outWidth;
          int inSampleSize = 1;
      
          if (height > reqHeight || width > reqWidth) {
              if (width > height) {
                  inSampleSize = Math.round((float)height / (float)reqHeight);
              } else {
                  inSampleSize = Math.round((float)width / (float)reqWidth);
              }
          }
          return inSampleSize;
      }
      
      public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
              int reqWidth, int reqHeight) {
      
          // First decode with inJustDecodeBounds=true to check dimensions
          final BitmapFactory.Options options = new BitmapFactory.Options();
          options.inJustDecodeBounds = true;
          BitmapFactory.decodeResource(res, resId, options);
      
          // Calculate inSampleSize
          options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
      
          // Decode bitmap with inSampleSize set
          options.inJustDecodeBounds = false;
          return BitmapFactory.decodeResource(res, resId, options);
      }
      

      Android 开发者网站Loading Large Bitmaps Efficiently 中解释了整件事

      【讨论】:

        猜你喜欢
        • 2014-11-20
        • 2013-08-25
        • 2017-08-23
        • 2017-11-26
        • 1970-01-01
        • 2011-04-07
        • 2020-06-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多