0

i'm exausted. I'm working on this all day. In my app i have 100 ImageViews, but i'm getting a java.outofmemory error so i decided to decode and resize file, but i can not manage it to work. Can someone take a look in the code and suggest me anything?

MainActivity code: public class MainActivity extends Activity implements OnClickListener {

ImageView display;
int toPhone;
private Context context;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    System.gc();
    context = this;
    toPhone = R.drawable.wal1;
    display = (ImageView) findViewById(R.id.WPdisplay);
    ImageView image1 = (ImageView) findViewById(R.id.WPimg1);
    ImageView image2 = (ImageView) findViewById(R.id.WPimg2);
    ImageView image3 = (ImageView) findViewById(R.id.WPimg3);
    ImageView image4 = (ImageView) findViewById(R.id.WPimg4);
    ImageView image5 = (ImageView) findViewById(R.id.WPimg5);
    Button setWall = (Button) findViewById(R.id.BsetWall);
    image1.setOnClickListener(this);
    image2.setOnClickListener(this);
    image3.setOnClickListener(this);
    image4.setOnClickListener(this);
    image5.setOnClickListener(this);
    setWall.setOnClickListener(this);
    }

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

public Bitmap decodeAndResizeFile(int resID) {
     try {
            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;

i think in the next line i have a problem because it says The value of the local variable bmp is not used

            Bitmap bmp= BitmapFactory.decodeResource(context.getResources(), resID, o);
           // BitmapFactory.decodeStream(new FileInputStream(f), null, o);

            // The new size we want to scale to
            final int REQUIRED_SIZE = 70;

            // Find the correct scale value. It should be the power of 2.
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true) {
                if (width_tmp / 2 < REQUIRED_SIZE
                        || height_tmp / 2 < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeResource(context.getResources(), resID, o2);

     } finally {

        }
    }

@Override
public void onClick(View v) {
    Bitmap bmpp;
    switch (v.getId()){
    case R.id.WPimg1:
         display.setImageResource(R.drawable.wal1);
         toPhone = R.drawable.wal1;

in the next line i'm trying to decode it(and i'm doing this for each image)

         bmpp = decodeAndResizeFile(toPhone);
         //Bitmap bmpp = decodeAndResizeFile(file);
         break;
    case R.id.WPimg2:
         display.setImageResource(R.drawable.wal2);
         toPhone = R.drawable.wal2;  
         bmpp = decodeAndResizeFile(toPhone);
        display.setImageBitmap(bmpp);
         break;
    case R.id.WPimg3:
         display.setImageResource(R.drawable.wal3);
         toPhone = R.drawable.wal3;
         bmpp = decodeAndResizeFile(toPhone);
         break;
    case R.id.WPimg4:
         display.setImageResource(R.drawable.wal4);
         toPhone = R.drawable.wal4;
         bmpp = decodeAndResizeFile(toPhone);
         break;
    case R.id.WPimg5:
         display.setImageResource(R.drawable.wal5);
         toPhone = R.drawable.wal5;
         bmpp = decodeAndResizeFile(toPhone);
         break;
    case R.id.BsetWall:
         try{
             WallpaperManager.getInstance(getApplicationContext()).setResource(toPhone);
             Toast.makeText(getApplicationContext(), "Wallpaper was set!", Toast.LENGTH_SHORT).show();
         } catch(IOException e) {
             e.printStackTrace();
             Toast.makeText(getApplicationContext(), "No privileges!", Toast.LENGTH_SHORT).show();
         }
         break;
    }
    }
    }

here is my Logcat

--- decoder->decode returned false
Shutting down VM
threadid=1: thread exiting with uncaught exception (group=0xb3adbba8)
FATAL EXCEPTION: main
Process: app.technozed.testwall, PID: 1250
java.lang.RuntimeException: Unable to start activity ComponentInfo{app.technozed.testwall/app.technozed.testwall.MainActivity}: android.view.InflateException: Binary XML file line #304: Error inflating class <unknown>
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: android.view.InflateException: Binary XML file line #304: Error inflating class <unknown>
at android.view.LayoutInflater.createView(LayoutInflater.java:620)
at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
at android.view.LayoutInflater.onCreateView(LayoutInflater.java:669)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:694)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:758)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:758)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:290)
at android.app.Activity.setContentView(Activity.java:1929)
at app.technozed.testwall.MainActivity.onCreate(MainActivity.java:28)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
... 11 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.constructNative(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at android.view.LayoutInflater.createView(LayoutInflater.java:594)
... 26 more
Caused by: java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:422)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:840)
at android.content.res.Resources.loadDrawable(Resources.java:2110)
at android.content.res.TypedArray.getDrawable(TypedArray.java:602)
at android.widget.ImageView.<init>(ImageView.java:129)
at android.widget.ImageView.<init>(ImageView.java:119)
... 29 more
George Lungu
  • 125
  • 2
  • 4
  • 16

1 Answers1

1

Where you have the warning 'The value of the local variable bmp is not used' in decodeAndResizeFile(), you can remove the 'Bitmap bmp =' part and leave it as:

BitmapFactory.decodeResource(context.getResources(), resID, o);

This is because this call is with o.inJustDecodeBounds = true. When that is the case, only the dimensions are returned in object 'o' and a null bitmap is returned (so there's no point storing it).

In your switch statement, change each case to be like the following:

case R.id.WPimg1:
    toPhone = R.drawable.wal1;
    bmpp = decodeAndResizeFile(toPhone);
    display.setImageBitmap(bmpp);
    break;

So here, you decode the resource into a scaled down bitmap and then assign that bitmap to the ImageView. You don't want or need to do this:

display.setImageResource(R.drawable.wal1);

...since you're risking an OutOfMemoryException and you go on to set 'display' with the scaled bitmap anyway. Having made those changes, your code works for me. I can see the wallpaper preview images and then set the wallpaper (don't forget the SET_WALLPAPER permission in the manifest).

That doesn't however explain your logcat which appears to be a problem in onCreate() inflating R.layout.activity_main. Towards the end of your logcat, it shows an OutOfMemoryError again. I can re-create this error if I assign a large jpg to an ImageView in the XML (an OOM results and the layout therefore cannot be inflated). You cannot scale down drawables 'on-the-fly' when they are assigned in XML, so create physically smaller drawables for use within the XML.

EDIT:

When you click on an image, 'display' gets a new bitmap and the old one is eligible for GC. Here is one approach for releasing the memory used by the old one right away, without waiting for GC to kick in:

Move this from onClick() to being a class variable instead:

Bitmap bmpp;  //Reference to the same bitmap that 'display' is using

Then in onClick():

Bitmap old_bm = null;  //Reference to hold the old bitmap
switch (v.getId()){
case R.id.WPimg1:
    old_bm = bmpp;  //Set to the bitmap currently being used by 'display'
    toPhone = R.drawable.wal1;
    bmpp = decodeAndResizeFile(toPhone);   //'display' now has a new bitmap
    display.setImageBitmap(bmpp);
    if (old_bm != null)
    {
        old_bm.recycle();  //Recyle the old bitmap
        old_bm = null;     //Clear the reference to allow GC to clean up fully
    }
    break;
NigelK
  • 8,255
  • 2
  • 30
  • 28
  • it doesnt work on a 16mb emulator, but it does work on a 1000 mb emulator. But there is a problem, the images are very small, i want them to fit the screen – George Lungu Jan 29 '14 at 11:50
  • You can give your ImageViews a fixed size and then set the scale type to determine how the image should be scaled to fit: http://developer.android.com/reference/android/widget/ImageView.ScaleType.html. I think it would be a case of the image being small enough not to cause OOM, but large enough to have a decent resolution when displayed. – NigelK Jan 29 '14 at 12:04
  • i so in my Logcat when clicking on images the Heap is growing faster then GC frees memory `Grow heap (frag case) to 46.873MB for 3240016-byte allocation` `Grow heap (frag case) to 47.902MB for 4320016-byte allocation` can i somehow set the bitmap to null when i have finished working with it? – George Lungu Jan 29 '14 at 12:15
  • You can't set the bitmap to null because 'display' is using it. When 'display' gets a new bitmap, the old one will be GC'd but that will not happen immediately. You can recycle the old bitmap yourself to release the memory sooner - see my edit for something to try. I'll be interested to know if it works for you. Any further issues, and I think it's time for a new question... thx and good luck. – NigelK Jan 29 '14 at 13:15