1

I got some OutOfMemory crashes when switching image in ImageSwitcher, it's very rare but it happens sometimes (never to me, otherway I would be able to debug it haha).

I only have 5 images (PNG) and each of them has something between 10-60kb, so to be honest I am quite surprised. Here is code:

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
}

Am I doing something wrong?

Stacktrace:

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)
qkx
  • 2,383
  • 5
  • 28
  • 50
  • 2
    Image size does not matter completely. Only thing that matters is the image resolution. So what is the image resolution? – Vladyslav Matviienko Jun 10 '17 at 12:15
  • 1
    also put the exception stacktrace here. Not only the line number can help to fix error. – Vladyslav Matviienko Jun 10 '17 at 12:16
  • @VladMatvienko hm, interesting. Resolution is indeed quite high - 620x1084. Is that a problem? – qkx Jun 10 '17 at 12:19
  • yes, that might really be the problem. So doesn't the log have lines like `failed to allocate xxxx bytes... with xxx until OOM`? Those xxx numbers matter, since they will show how much memory will consume your image in Android RAM, so we can see if it is too large. As a quick fix, you can try using **Adil** solution with largeHeap. – Vladyslav Matviienko Jun 10 '17 at 12:21
  • also the actual device, on which you try to do that matters. How much RAM it has, which version of Android, and which screen resolution. Since the Dalvic/ART java virtual machine on the device is usually optimized for certain RAM size and screen resolution the way that if the device has small screen resolution, it won't let you load too large images since it does not need such images, and it will enforce you to load images pre-resized. – Vladyslav Matviienko Jun 10 '17 at 12:24
  • There are some devices where the usable RAM for an app is really small. If your images are 620x1084 they are not small at all, they are big images – MatPag Jun 10 '17 at 12:25
  • no, there is no allocation problem. This is all stacktrace...Well, I will try to reduce resolution. I think setting largeHeap to large is overkill :) EDIT: funny think is, it crashes rarely but mostly on relatively new, modern devices with enough RAM (2-3GB) – qkx Jun 10 '17 at 12:25
  • @qkx Have you tried with the answer i added? – MatPag Jun 11 '17 at 19:52
  • use third party library which support caching of drawable. Here is one to look https://github.com/nostra13/Android-Universal-Image-Loader – Keyur Thumar Jun 12 '17 at 06:26

4 Answers4

2

With the below code, you can be able to reduce the resolution of the Bitmap prior to loading it in the 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);
}

These two methods below were copied from developer site 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
  • 41,742
  • 14
  • 105
  • 114
  • hey MatPag, thanks for your code, but I already solved problem in other (very similar) way, see answer below. – qkx Jun 12 '17 at 06:14
1

unfortunately if None of the Above works, then Add this to your Manifest file. Inside application tag

 <application
         android:largeHeap="true"

for details
Strange out of memory issue while loading an image to a Bitmap object

0

Add android:largeHeap="true"

in your Manifest file.

hope solve your issue.

Adil Saiyad
  • 1,582
  • 2
  • 17
  • 34
  • I do not like this solution...My app is small, it does not do any extensive work or anything, I only have imageswitcher with 5 images, so I would like to fix the root of the problem. – qkx Jun 10 '17 at 12:21
0

So, solution was to decrease resolution of images (I did manual resizing in photoshop). From 620x1080 to 800x460 and everything works fine.

Conclusion: Image size it's not that important - image resolution is.

PS: MatPag should also be teoretically correct, it's practically same what I did - except he is doing that in code, I did it manually in photoshop by myself. But I think doing that manually is better IMHO.

qkx
  • 2,383
  • 5
  • 28
  • 50
  • This could be a good choice, which obviously depends where you are using the images. If you are showing them in fullscreen, maybe you will have a problem of quality in bigger phone with high resolution support. While with the code approach you let the system trying to find the best resolution for the current device. If you are using the images in small views i think you won't have any problems – MatPag Jun 12 '17 at 08:28