2

Basically i'm adding a wallpaper picker for android 4.4.2 lockscreen background and when the image is set and i turn the screen off then back on to view the lockscreen my screen is going black and logcat is giving me an out of memory allocation error. So far i have tried using Bitmap decodeFile(String pathName) and i also rebased to use Bitmap decodeFile(String pathName, Options opts) but the result is the same every time...

Here is the original method used to set the image:

private static final String WALLPAPER_IMAGE_PATH =
        "/data/data/com.android.settings/files/lockscreen_wallpaper.png";

private KeyguardUpdateMonitorCallback mBackgroundChanger = new KeyguardUpdateMonitorCallback() {
    @Override
    public void onSetBackground(Bitmap bmp) {
        if (bmp != null) {
            mKeyguardHost.setCustomBackground(
                    new BitmapDrawable(mContext.getResources(), bmp));
        }
        else {
            File file = new File(WALLPAPER_IMAGE_PATH);
            if (file.exists()) {
                mKeyguardHost.setCustomBackground(
                        new BitmapDrawable(mContext.getResources(), WALLPAPER_IMAGE_PATH));
            }
            else {
                mKeyguardHost.setCustomBackground(null);
            }
        }
        updateShowWallpaper(bmp == null);
    }
};

which is being called from case 1 in:

    public void setCustomBackground(Drawable d) {
        if (!mAudioManager.isMusicActive()) { 

            int mBackgroundStyle = Settings.System.getInt(mContext.getContentResolver(),
                    Settings.System.LOCKSCREEN_BACKGROUND_STYLE, 2);
            int mBackgroundColor = Settings.System.getInt(mContext.getContentResolver(),
                    Settings.System.LOCKSCREEN_BACKGROUND_COLOR, 0x00000000);
            switch (mBackgroundStyle) {
                case 0:
                    d = new ColorDrawable(mBackgroundColor);
                    d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER);
                    mCustomBackground = d;
                    break;
                case 1:
                    KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
                    break;
                case 2:
                default:
                    mCustomBackground = d;
            }
            computeCustomBackgroundBounds(mCustomBackground);
            setBackground(mBackgroundDrawable);
        }

        if (!ActivityManager.isHighEndGfx()) {
            mCustomBackground = d;
            if (d != null) {
                d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER);
            }
            computeCustomBackgroundBounds(mCustomBackground);
            invalidate();
        } else {
            if (getWidth() == 0 || getHeight() == 0) {
                d = null;
            }
            if (d == null) {
                mCustomBackground = null;
                setBackground(mBackgroundDrawable);
                return;
            }
            Drawable old = mCustomBackground;
            if (old == null) {
                old = new ColorDrawable(0);
                computeCustomBackgroundBounds(old);
            }

            d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER);
            mCustomBackground = d;
            computeCustomBackgroundBounds(d);
            Bitmap b = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b);
            mBackgroundDrawable.draw(c);

            Drawable dd = new BitmapDrawable(b);

            mTransitionBackground = new TransitionDrawable(new Drawable[]{old, dd});
            mTransitionBackground.setCrossFadeEnabled(true);
            setBackground(mTransitionBackground);

            mTransitionBackground.startTransition(200);

            mCustomBackground = dd;
            invalidate();
        }

        if (d != null) {
            d.setColorFilter(BACKGROUND_COLOR, PorterDuff.Mode.SRC_OVER);
        }
        computeCustomBackgroundBounds(mCustomBackground);
        invalidate();
    }

this is my logcat output:

I/dalvikvm-heap(13100): Forcing collection of SoftReferences for 16571536-byte allocation
E/dalvikvm-heap(13100): Out of memory on a 16571536-byte allocation.
I/dalvikvm(13100): "main" prio=5 tid=1 RUNNABLE
I/dalvikvm(13100):   | group="main" sCount=0 dsCount=0 obj=0x4159fe40 self=0x414d4548
I/dalvikvm(13100):   | sysTid=13100 nice=0 sched=0/0 cgrp=apps handle=1074098536
I/dalvikvm(13100):   | state=R schedstat=( 0 0 0 ) utm=877 stm=93 core=1
I/dalvikvm(13100):   at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
I/dalvikvm(13100):   at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:613)
I/dalvikvm(13100):   at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:589)
I/dalvikvm(13100):   at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:369)
I/dalvikvm(13100):   at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:395)
I/dalvikvm(13100):   at com.android.keyguard.KeyguardViewManager$1.onSetBackground(KeyguardViewManager.java:127)
I/dalvikvm(13100):   at com.android.keyguard.KeyguardUpdateMonitor.dispatchSetBackground(KeyguardUpdateMonitor.java:452)
I/dalvikvm(13100):   at com.android.keyguard.KeyguardViewManager$ViewManagerHost.setCustomBackground(KeyguardViewManager.java:302)

nothing i have tried as of yet has worked, any ideas?

EDITED

to further clarify this is what sets the image in Settings:

        } else if (requestCode == REQUEST_PICK_WALLPAPER) {
            FileOutputStream wallpaperStream = null;
            try {
                wallpaperStream = getActivity().openFileOutput(WALLPAPER_NAME,
                        Context.MODE_WORLD_READABLE);

            } catch (FileNotFoundException e) {
                return; // NOOOOO
            }
            Uri selectedImageUri = getLockscreenExternalUri();
            Bitmap bitmap;
            if (data != null) {
                Uri mUri = data.getData();
                try {
                    bitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(),
                            mUri);
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, wallpaperStream);

                    Toast.makeText(getActivity(), getResources().getString(R.string.
                            background_result_successful), Toast.LENGTH_LONG).show();
                    Settings.System.putInt(getContentResolver(),
                            Settings.System.LOCKSCREEN_BACKGROUND_STYLE, 1);
                    updateVisiblePreferences();

                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    bitmap = BitmapFactory.decodeFile(selectedImageUri.getPath());
                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, wallpaperStream);
                } catch (NullPointerException npe) {
                    Log.e(TAG, "SeletedImageUri was null.");
                    Toast.makeText(getActivity(), getResources().getString(R.string.
                            background_result_not_successful), Toast.LENGTH_LONG).show();
                    super.onActivityResult(requestCode, resultCode, data);
                    return;
                }
            }

        }
cphelps76
  • 75
  • 1
  • 2
  • 10
  • Perhaps try with another, smaller image? – initramfs Feb 07 '14 at 20:39
  • well the problem is that it is a user selectable image chosen from gallery, so the image needs to somehow be compressed...the option to set a custom lockscreen background is relatively useless if a normal image can't be loaded – cphelps76 Feb 07 '14 at 20:42
  • I don't understand why you are trying to compress the image to a PNG, it does nothing in reducing the memory footprint of the image, and if the image is a picture, the resulting PNG file would be much larger than the original JPG file. – Kai Feb 08 '14 at 03:10
  • 1
    But the main problem is that you are trying to allocate a Bitmap that takes 16MB, which implies a 4M pixel image. So the question is whether the screen is capable of displaying a 4MPixel image in its native resolution. Since only Nexus 10 has such a high resolution I'd say no. If you don't want to use 3rd party lib, just use the first link provided by @Sharj, the solution there rescales the image to what's required. – Kai Feb 08 '14 at 03:11

1 Answers1

7

You are running out of memory because you are not handling images carefully. Take a look at following question/answer for a better idea what happens when you try to load an image: Strange out of memory issue while loading an image to a Bitmap object

If you are a new developer then I would recommend you to just use any good library that does loading part for you otherwise it would be very hard to load images.

Android-Universal-Image-Loader: Widely used library https://github.com/nostra13/Android-Universal-Image-Loader

Alternative Picasso Library: One line of code does the job for you: http://square.github.io/picasso/

Updated:

Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the per-app limit on some devices.

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

Community
  • 1
  • 1
Sharjeel
  • 15,588
  • 14
  • 58
  • 89
  • i added the code i'm using in settings to set the image to help clarify how i'm handling the image – cphelps76 Feb 07 '14 at 21:05
  • could public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream) be used to compress the image? – cphelps76 Feb 07 '14 at 22:26
  • The problem is not the function the problem is the size. Your device runs out of memory. Even if you optimize your solution it is not guaranteed that it will run on all devices. It's a better option to use a library that has been developed and used by top developers. It's just one line of code. – Sharjeel Feb 07 '14 at 22:54
  • i agree but i doubt the rest of the team would be on board for using non google code for this sort of considering this exact same thing worked without issue prior to 4.4.2...but the above update looks like it gives me what i need – cphelps76 Feb 07 '14 at 23:19