【问题标题】:Android ImageSwitcher - OutOfMemory when switching images (very small)Android ImageSwitcher - 切换图像时的OutOfMemory(非常小)
【发布时间】:2017-06-10 12:10:09
【问题描述】:

在 ImageSwitcher 中切换图像时,我遇到了一些 OutOfMemory 崩溃,这种情况非常罕见,但有时会发生(对我来说从来没有,否则我可以调试它哈哈)。

我只有 5 张图片 (PNG),每张图片的大小都在 10-60kb 之间,所以说实话我很惊讶。这是代码:

frameImages = new int[]{R.drawable.f_0, R.drawable.f_1, R.drawable.f_2, R.drawable.f_3, R.drawable.f_4};

...
public void switchImage() {
    int frame = getFrame();
    int image = frameImages[frame];

    // imageSwitcher.startAnimation(getAnimation());
    if (frame == 0)
        startYAxisRotation(imageSwitcher, 400);

    imageSwitcher.setImageResource(image); //CRASHES HERE
}

我做错了吗?

堆栈跟踪:

java.lang.OutOfMemoryError: 
  at android.graphics.BitmapFactory.nativeDecodeAsset(BitmapFactory.java:0)
  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:677)
  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:507)
  at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:872)
  at android.content.res.Resources.loadDrawable(Resources.java:3022)
  at android.content.res.Resources.getDrawable(Resources.java:1586)
  at android.widget.ImageView.resolveUri(ImageView.java:648)
  at android.widget.ImageView.setImageResource(ImageView.java:377)
  at android.widget.ImageSwitcher.setImageResource(ImageSwitcher.java:41)
  at com.myapp.MainActivity$5.switchImage(MainActivity.java:143)
  at android.os.Handler.handleCallback(Handler.java:733)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:157)
  at android.app.ActivityThread.main(ActivityThread.java:5293)
  at java.lang.reflect.Method.invokeNative(Method.java:0)
  at java.lang.reflect.Method.invoke(Method.java:515)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
  at dalvik.system.NativeStart.main(NativeStart.java:0)

【问题讨论】:

  • 图像大小完全无关紧要。唯一重要的是图像分辨率。那么图像分辨率是多少?
  • 还将异常堆栈跟踪放在这里。不仅行号可以帮助修复错误。
  • @VladMatvienko 嗯,很有趣。分辨率确实很高——620x1084。有问题吗?
  • 是的,这可能真的是问题所在。那么日志中没有像failed to allocate xxxx bytes... with xxx until OOM 这样的行吗?这些 xxx 数字很重要,因为它们会显示在 Android RAM 中将消耗多少内存,因此我们可以查看它是否太大。作为快速修复,您可以尝试将 Adil 解决方案与 largeHeap 一起使用。
  • 也是您尝试执行此操作的实际设备。它有多少 RAM,哪个版本的 Android 以及哪个屏幕分辨率。由于设备上的 Dalv/ART java 虚拟机通常针对特定的 RAM 大小和屏幕分辨率进行优化,如果设备的屏幕分辨率较小,它不会让您加载太大的图像,因为它不需要这样的图像,它会强制你加载预先调整大小的图像。

标签: android out-of-memory imageswitcher


【解决方案1】:

使用以下代码,您可以在将 Bitmap 加载到 ImageSwitcher 之前降低其分辨率。

public void switchImages(){
    //Create a Bitmap with the resolution based on the view size.
    //getWidth and getHeight will not work until the layout as finished,
    //if you need to show an image when the Activity is created you need
    //to follow this link: https://stackoverflow.com/a/6569243/2910520
    Bitmap smallBitmap = decodeSampledBitmapFromResource(getResources(), yourDrawableId, 
                  imageSwitcher.getWidth(), imageSwitcher.getHeight())
    //use a drawable, because you can create it from a custom Bitmap
    BitmapDrawable drawable = new BitmapDrawable(getResources(), smallBitmap);
    imageSwitcher.setImageDrawable(drawable);
}

以下这两种方法是从开发者网站here复制而来的

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);
}

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) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

【讨论】:

  • 嘿 MatPag,感谢您的代码,但我已经以其他(非常相似)的方式解决了问题,请参阅下面的答案。
【解决方案2】:

很遗憾,如果上述方法均无效,请将其添加到您的清单文件中。内部应用标签

 <application
         android:largeHeap="true"

了解详情
Strange out of memory issue while loading an image to a Bitmap object

【讨论】:

    【解决方案3】:

    添加 android:largeHeap="true"

    在您的清单文件中。

    希望能解决您的问题。

    【讨论】:

    • 我不喜欢这个解决方案...我的应用程序很小,它没有做任何大量的工作或任何事情,我只有 5 个图像的图像切换器,所以我想修复问题。
    【解决方案4】:

    因此,解决方案是降低图像的分辨率(我在 Photoshop 中手动调整大小)。从 620x1080 到 800x460,一切正常。

    结论:图像大小并不重要——图像分辨率才是。

    PS:MatPag 理论上也应该是正确的,这实际上与我所做的相同 - 除了他在代码中这样做,我自己在 photoshop 中手动完成。但我认为手动执行此操作更好恕我直言。

    【讨论】:

    • 这可能是一个不错的选择,这显然取决于您使用图像的位置。如果您以全屏方式显示它们,那么在具有高分辨率支持的较大手机中可能会出现质量问题。使用代码方法时,您可以让系统尝试为当前设备找到最佳分辨率。如果您在小视图中使用图像,我认为您不会有任何问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-10
    • 1970-01-01
    • 2013-01-09
    • 2013-03-30
    • 1970-01-01
    • 2014-08-10
    相关资源
    最近更新 更多