0

I was getting out of memory errors loading custom images. I read https://developer.android.com/training/displaying-bitmaps/load-bitmap.html for assistance.

I'm following the example to decode the stream to get size information first, then decode. Still crashing on that first decoding. Is there a way around this?

ava.lang.OutOfMemoryError: Failed to allocate a 48771084 byte allocation with 16776928 free bytes and 25MB until OOM BackgroundImageManager.java, line 84

dalvik.system.VMRuntime.newNonMovableArray Native Method 2 android.graphics.BitmapFactory.nativeDecodeStream Native Method 3 android.graphics.BitmapFactory.decodeStreamInternal BitmapFactory.java, line 882 4 android.graphics.BitmapFactory.decodeStream BitmapFactory.java, line 858 5 android.graphics.BitmapFactory.decodeStream BitmapFactory.java, line 896 6 com.myapp.Utils.BackgroundImageManager.background BackgroundImageManager.java, line 8

public class BackgroundImageManager {
    private final static String TAG = BackgroundImageManager.class.getSimpleName();
    private static InputStream currentBackgroundImage;

    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;
            }
        }
        Log.v("Biscuit-Sample", String.valueOf(inSampleSize));
        if (inSampleSize < 4) {
            inSampleSize = 4;
        }
        return inSampleSize;
    }

    public static Drawable background(Context context, Store store) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

        String bgUri = null;
        int bgId = 0;
        if (store != null) {
            bgUri = store.backgroundImageURI;
            bgId = store.backgroundImageNumber;
        }

        if (currentBackgroundImage != null) {
            try {
                currentBackgroundImage.close();
                Log.v(TAG, "Current background image closed.");
            } catch (IOException e) {
                Log.e(TAG, "Could not close background image.", e);
            }
        }

        if(bgUri != null && !bgUri.isEmpty()) {
            try {
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;

                Activity activity = (Activity) context;
                Display display = activity.getWindowManager().getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);
                int width = size.x;
                int height = size.y;
                BitmapFactory.decodeStream( context.getContentResolver().openInputStream(Uri.parse(bgUri)) );
                options.inSampleSize = BackgroundImageManager.calculateInSampleSize(options, width, height);
                Bitmap bitmap = BitmapFactory.decodeStream( context.getContentResolver().openInputStream(Uri.parse(bgUri)) );
                Drawable d = new BitmapDrawable(context.getResources(), bitmap);
                return d;
            } catch (FileNotFoundException e) {
                Log.e(TAG, "Custom background image file could not be found.", e);
            } catch (IOException e) {
                Log.e(TAG, "Could not close custom background image after creating drawable", e);
            }
        }
        if(bgId != 0) {
            try {
                return context.getResources().getDrawable(bgId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return context.getResources().getDrawable(R.drawable.bg_default);
    }

2 Answers 2

1

To handle bitmpas you can use one of the many opensource libraries available. E.g Fresco

to your issue:

First you are decoding the same bitmap twice.

BitmapFactory.decodeStream( context.getContentResolver().openInputStream(Uri.parse(bgUri)) );
options.inSampleSize = BackgroundImageManager.calculateInSampleSize(options, width, height);
Bitmap bitmap = BitmapFactory.decodeStream( context.getContentResolver().openInputStream(Uri.parse(bgUri)) );

It is probably a wrong copy/paste. In the first line the bitmap is decode and not used. Delete the first BitmapFactory.decodeStream

the problem lies here

Bitmap bitmap = BitmapFactory.decodeStream( context.getContentResolver().openInputStream(Uri.parse(bgUri)) );

it should be

Bitmap bitmap = BitmapFactory.decodeStream( context.getContentResolver().openInputStream(Uri.parse(bgUri)), null, options);

the option's object has to be part of the method's call in order to be used.

Sign up to request clarification or add additional context in comments.

Comments

0

Better way to manage images is with the Picasso library because it manages cache and ram, and therefore avoiding OutOfMemory crash.

Example: Picasso.with(Context).load("your_url").into(yourImageView);

More info here: Picasso library

3 Comments

@an_android_dev will I have to open an input stream, or can I jus use Uri.parse(bgUri) instead of "your_url"?
@quantumpotato you can use url String. For example : load("example.com/myimage.jpeg");
@an_android_dev I will try this. The URI I have is local to the device

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.