Showing posts with label images. Show all posts
Showing posts with label images. Show all posts

Saturday, December 10, 2011

OutOfMemory errors when populating a Gallery


I've just spent the last couple of hours wrestling with OutOfMemory errors when populating a Gallery widget with photos, and as fun as it's been, I think I've sorted it out and thought I'd share my solution with you.

For starters, the reason we're getting OutOfMemory errors in the first place is that the heap size on Android devices is limited to something like 16 MB on a G1 and 24 MB on a Nexus one.

As soon as you start to work with media files you quickly start to learn that you need to work to manage your memory usage with Android as a result of this limitation.

I would also like to thank direct to device debugging, I couldn't have done this without utilizing it, I would go as far as saying that it is essential in situations like this.

Here is the full code of one of my imageAdapters,  I'm using this to provide the images to my Gallery widget like this:

galleryTop.setAdapter(new ImageAdapterTop(this));


The Adapter class :

public class ImageAdapterTop extends BaseAdapter {
   int mGalleryItemBackground;
   private Context mContext;


    File[] allTopSlices = Utils.getAllTopSlices();  
    String[] fullPathAllTopSlices = new String[allTopSlices.length];
    Uri[] uriAllTopSlices = new Uri[fullPathAllTopSlices.length];


   public ImageAdapterTop(Context c) {
       mContext = c;
       TypedArray a = obtainStyledAttributes(R.styleable.HelloGallery);
       mGalleryItemBackground = a.getResourceId(
               R.styleable.HelloGallery_android_galleryItemBackground, 0);
       a.recycle();
   }


   public int getCount() {
       return uriAllTopSlices.length;
   }


   public Object getItem(int position) {
       return position;
   }


   public long getItemId(int position) {
       return position;
   }


   public View getView(int position, View convertView, ViewGroup parent) {
    int screenHeightPx = Utils.GetScreenHeight(getWindowManager().getDefaultDisplay());
   
    for(int i = 0; i < allTopSlices.length; i++){
    fullPathAllTopSlices[i] = allTopSlices[i].getAbsolutePath();
    }
   
    for(int j=0; j < fullPathAllTopSlices.length; j++){
    uriAllTopSlices[j] = Uri.parse(fullPathAllTopSlices[j]);
    }
   
       ImageView i = new ImageView(mContext);
     
       recycleDrawable(i);


       i.setImageBitmap(Utils.readBitmap(uriAllTopSlices[position].toString()));
     
       i.setLayoutParams(new Gallery.LayoutParams(400, (screenHeightPx-50) /3));
       i.setScaleType(ImageView.ScaleType.FIT_XY);      
       i.setBackgroundResource(mGalleryItemBackground);


       System.gc();
       return i;
     
   }
}



and the referenced recycleDrawable :

private void recycleDrawable(ImageView i) {
BitmapDrawable currentBitmapDrawable = (BitmapDrawable)i.getDrawable();

if(currentBitmapDrawable != null){
currentBitmapDrawable.getBitmap().recycle();        
}
        System.gc();
}



and readBitmap methods:

public static Bitmap readBitmap(String selectedImage) {
Bitmap bm = null;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;


try {
bm = BitmapFactory.decodeFile(selectedImage, options);
}
catch (OutOfMemoryError e) {
e.printStackTrace();
}

return bm;
}

Probably worth mentioning that the 'options.inSampleSize' reference, which is downsizing the image is pretty much essential when working with large files with the memory constraints associated with current Android devices.

Hope that helps someone out there.


Sunday, November 20, 2011

'An error has occurred' when working with Bitmaps



On a recent project I needed to rotate and slice images taken from the camera, and I found that I was getting regular, but annoying to track down 'an error has occurred' messages.

I couldn't see anything in particular that was wrong with my code, and what made it worse was that I wasn't receiving any errors at all in the emulator, only when I deployed the app to my phone for testing.

It took a few hours to figure out what was going on, so I thought I'd share it here and maybe save somebody else some time.

Like a lot of errors, it seemed painfully obvious after the fact, but the cause was I was basically running out of memory when creating the bitmap objects below. From my experience, you can only create one or two bitmap objects before you will encounter issues like this, because the heap size on Android devices is limited to something like 16 MB on a G1 and 24 MB on a Nexus one.
.. That's not a lot of 6 MB photos.

Luckily, it's super easy to fix.
All you need to do is recycle the bitmaps once you've finished with them, as per below code, and as pointed out by Romain Guy here,

"..The problem is that it can take a couple of GC cycles for a Bitmap to be properly released on Android before Android 3.x. Even if you call recycle() I believe the bitmap counts against your heap usage until at least the next GC. This is one of the very few situations where I would advise you to force a GC by calling System.gc()."

Matrix mat = new Matrix();
mat.postRotate(90);

Bitmap photoRotated = Bitmap.createBitmap(bitmapToSave, 0, 0, bitmapToSave.getWidth(), bitmapToSave.getHeight(), mat, false);

Bitmap photoCut1 = Bitmap.createBitmap(photoRotated, 0, 0, photoRotated.getWidth(), heightOfSlice);

photoRotated.recycle();

//save photoCut1 bitmap here

photoCut1.recycle();
System.gc();


So don't forget to recycle and call System.gc() a couple of times when working with large files like images and hopefully, like me, your errors messages will disappear.

Monday, February 7, 2011

Wino the Wine Advisor now updated, new features & grrraphics


I've recently released an update for my Wino the Wine Advisor app,
The new version now allows you to combine tastes and also has some great new wine bottle graphics (even if I do say so myself).

Here are some screenshots:





.. And I have to say, those wine bottle images were an absolute pain to create.

I use Gimp and Inkscape to create all my graphics, in case you were wondering.




Sunday, January 23, 2011

How to get images dynamically


Lets say you're storing items in a slqite db for your new, soon-to-be-award-winning application, and one of the columns you're storing is the name of the image to associate with that item.

How on earth do you reference it from within your app so you can display it?
Good question! Here's how:


//first get a reference to the ImageView in our XML layout file we want to display the image in..

ImageView imgVw = (ImageView) customView.findViewById(R.id.imgVwInXML);

//Next check that there is actually an image file name in the db column:

if (OurDBCursor.getString(OurDBCursor.getColumnIndex("imageName")) != null) 
{
 
//if there is a name for the image, get that name..

  myImageName  = OurDBCursor.getString(OurDBCursor.getColumnIndex("imageName"));  
 

//get a reference to the image (located in our drawable folder in our project):
          
  int resID = getResources().getIdentifier("com.BlueMongo.Test:drawable/" + myImageName, null, null);  
 

//assign the resouce with that ID to our ImageView:
               
  imgVw.setImageResource(resID);
}

.. And that's it.

Hope that helps. Have a great day.