【问题标题】:Android - Too much memory consumed by changing ImageView drawableAndroid - 更改 ImageView 可绘制对象消耗的内存过多
【发布时间】:2016-01-16 11:06:17
【问题描述】:

在我的 Android 应用程序中,我有一个自定义 ImageView,它需要根据用户交互来更新其图像。 ImageView 只能有两张图片,分别代表一张卡片的背面和正面。 因此,当用户单击按钮时,我需要显示卡片的背面(将自定义的 ImageView 图像设置到卡片的背面,当用户再次单击按钮时,将正面图像设置为 ImageView 可绘制)。

为了实现这一点,我在自定义 ImageView 中有两种方法:

public void showBack() {
    setImageResource(R.drawable.card_back);
}

public void showFront() {
    setImageResource(R.drawable.card_front);
}

当然,这很好用,但我的应用程序的内存消耗有时会接近 114 MB,并且随着用户单击按钮(旋转卡片)而增加。

因此,我认为问题可能是由于图像的变化而产生的。 图片很小,一个是 104.6KB,另一个是 80.4KB(PNG 格式),所以我不明白为什么我的应用程序会消耗这么多内存(可能是因为图像被分配了很多次并且从不回收?或者可能是因为Android的drawables缓存系统?)

那么,我该怎么做才能解决我的问题呢?

谢谢。

图片尺寸为 335 x 501

【问题讨论】:

  • 当我将大图像显示为按钮背景时,我遇到了同样的问题。我为减少内存使用量所做的是缩小我使用的图像并在 xml 中重新创建具有形状的简单图像。网上有人建议在使用任何位图之前先做 System.gc,但我的伙伴认为我们应该让 Java 处理 GC,因为这就是它的全部意义......
  • 可能的原因:如果您将图像存储在可绘制文件夹中,Android 会将它们视为 mdpi 并放大它们。将它们放在 xhdpi 文件夹中。
  • 我注意到同样的现象,相对较小的 jpeg 可绘制对象加载到 ViewPager 中。我在 Drawable 文件夹中找到了 jpeg,它们正在创建 90MB 的内存分配。我将文件移动到 xxxhdpi 文件夹中,分配大约 6MB。它看起来仍然很多,但正如@black 所提到的,它产生了巨大的变化。

标签: android image android-imageview android-image


【解决方案1】:

所以这里有几个因素在起作用。 imageView 尺寸(我假设)已在 dp 中定义。根据屏幕分辨率,这转换为不同手机的像素数会有所不同。因此,相同的drawable,当附加到不同手机中的imageView时,必须缩放以匹配在imageView中显示它所需的px

此缩放需要大量内存,这会导致 OOM 异常。有些手机的 RAM 很大,可以在没有任何问题的情况下完成这种缩放,而旧型号可能无法做到。

当你加载到imageView 时,原始图像会保存在内存中,然后重新缩放。我建议获取imageView 的像素尺寸,然后相应地缩放图像,以产生最佳拟合。这将减少内存消耗。

This answerthis 应该可以帮助您获取 imageView 的尺寸。

Bitmap icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_resource);

     private Bitmap ScaleImage(Bitmap bitmap){
        //the original dimension of the bitmap
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        int requiredWidth=0;
        int requiredHeight=0;
        //todo: Calculate the required dimensions of the bitmap as per the imageView

        ExifInterface exif;
        Matrix matrix = new Matrix();
        /* In case you need to rotate the bitmap
        try {
            exif = new ExifInterface(value);
            int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
            Log.d("EXIF", "Exif: " + orientation);
            if (orientation == 6) {
                matrix.postRotate(90);
                Log.d("EXIF", "Exif: " + orientation);
            } else if (orientation == 3) {
                matrix.postRotate(180);
                Log.d("EXIF", "Exif: " + orientation);
            } else if (orientation == 8) {
                matrix.postRotate(270);
                Log.d("EXIF", "Exif: " + orientation);}

        }catch (IOException e){e.printStackTrace();}
       */

        matrix.postScale(requiredWidth/originalWidth, requiredHeight/originalHeight);
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,width, height, matrix, true);
        return resizedBitmap;
    }

最后

public void showFront() {
    Bitmap icon = BitmapFactory.decodeResource(context.getResources(),
    R.drawable.icon_resource);
    Bitmap scaledBitmap = ScaleImage(icon);
    setImageResource(scaledBitmap);
}

现在理想情况下,您希望将图像的缩放比例转换为Async Task,这样主线程就不会被阻塞。 请让我知道这是否有效。

【讨论】:

    【解决方案2】:

    图像的大小在这里没有太大的作用。另一方面,尺寸可以。因此,如果您使用大尺寸的图像并在 ImageView 中以较小的分辨率使用它们,那么这将是一个问题,您最终可能会遇到 OOM(Out Of Memory Exception)。

    请看: http://developer.android.com/training/displaying-bitmaps/index.html

    【讨论】:

    • 图片尺寸为 335 x 501
    • 尝试缓存图像,因为您正在翻转卡片并且图像应该被重复使用。从我提供的链接中获取帮助。
    • 您可以使用 FFImageLoading 进行缓存和缩减/优化内存使用。
    • @DanielLuberda 是的,但我正在用 Java 编写这个应用程序,而不是 Xamarin。我想我会选择链接中提供的解决方案。我想我也可以试试 Picasso 或 Glide,也许
    • 使用 UIL(通用图像加载器)。它有更好的文档。
    【解决方案3】:

    根据我在加载图像时的个人经验,您应该始终使用 picasso 库。它有助于解决所有内存问题,让您轻松扩展等。 http://square.github.io/picasso/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多