Friday, September 30, 2011

Android Bitmap heap

If you have been developing an app for Android that uses a lot of Images (I wonder which app does not), then very likely you have faced
OutOfMemoryError: bitmap size exceeds VM budget.
Then you probably tried to see who is using memory using DDMS and got a big surprise that heap size usage is much smaller than you expected. It could be that you saw the app heap was less than 4MB when you got out of memory error.

The interesting fact is, Android decided to handle the bitmaps differently in memory. The bitmap pixel data were not stored in java application heap, but rather in native memory. That's why those big chunks of data were not shown in the heap. Although when android is calculating the max allowed memory, it is counting those native memory allocations (and you get OutOfMemoryError even when heap is not full).

Although the pixel data are stored differently, typically you don't need to free those memory manually. If no one is referencing to the bitmap, pixel data will be automatically cleared up with the bitmap in next garbage collection cycle. Advanced developers can use
Bitmap.recycle()
method to clear those. Recycle does not clear the native memory immediately, rather marks it for garbage collector, so that on next gc run it's cleared.

Good news is things have changed at Honeycomb. Honeycomb onwards bitmap payloads are stored in the heap, as you would normally expect. Ahh, it's so nice to debug stuffs and see exactly how the memory is being consumed by those bitmaps.


Thursday, September 29, 2011

Weak Reference

For performance reasons, you typically want to keep the cpu expensive stuffs loaded in cache, so that everytime that does not need to be computed. Caching the images is perhaps the most common example.

But you surely don't want to keep that heavy images loaded forever. At some point, when no body is using it, you want it removed. A non-strong reference, for example, Soft reference or Weak reference, comes very handy in caching. Unfortunately CLDC1.1 does not support soft reference, you can rely on weak reference only. CLDC 1.0 supports none (but probably none but grandpas use CLDC1.0 devices, and hopefully you are not writing an app for them).

Using a Weak reference cannot be any simpler.


WeakReference weakRef = new WeakRefernce(null);

public void putToCache(Image data) {

   weakRef = new WeakReference(data);

}



public Object getUsingCache() {

  Image data = (Image) weakRef.get();

  if (data == null) {

     data = createImage();

     putToCache(data);

  }

  return data;

}


By the way, For Android, there is a little (!) problem... check http://code-gotcha.blogspot.com/2011/09/softreference.html

Disable Dismiss command in Nokia alert

Did you ever had this problem that there is an unexpected 'dismiss' command in nokia alerts while writing a j2me app?

I found this very annoying. I had an app where I wanted to show an infinite progress alert, and I didnt want to have any cancel button there (it was a short save operation).

I tried adding a command with space, empty string to replace the default dismiss command, but did not work. Finally the solution that worked for me was:
  • Add a command with string "\u200b" (zero width space)
  • add an empty commandlistener
Code snippet:



/**
* Creates and returns an Alert with the message. 
* If noOptionForUser/code> is set as true, 
* Alert will have it's own CommandHandler to consume all
* user events, Alert will have an 'invisible' Command to remove DISMISS_COMMAND
* and timeout will be set as Alert.FOREVER. If noOptionForUser/code> is false,
* it will just add the gauge with indefinite progress alert.
* @param message message to be displayed
* @param noOptionForUser if true, user is not given any option to choose. 
* Alert will have it's own commandHandler to consume all events.
* @return an Alert object with an INDEFINITE mode gauge attached to it
*/
public static Alert getProgressAlert(String message, boolean noOptionForUser)
{
  Alert alert = new Alert(null,message, null, AlertType.INFO);
  Gauge gauge = new Gauge(null, false, Gauge.INDEFINITE,
                                           Gauge.CONTINUOUS_RUNNING);
  alert.setIndicator(gauge);
  if(noOptionForUser) {
     alert.setCommandListener(new CommandListener() {
           public void commandAction(Command cmd, Displayable disp) {
           }
     });
     alert.setTimeout(Alert.FOREVER);
     alert.addCommand(new Command("\u200B", Command.OK, 1));//we dont like to see the DISMISS command, so adding an invisible command
  }
  return alert;
}

Wednesday, September 28, 2011

SoftReference problem in Android


Looks like Android VM does not honor the soft reference properly.

To improve the UI performance, I had written a two level cache for our Android and J2ME app. One cache stores the downloaded data (mostly images) to disk, I call it persistent cache. Another cache keeps the Bitmaps in heap, intuitively called runtime image cache.

When an UI element needs an image, app first checks in the runtime cache, if not found, checks the persistent cache (and puts that to runtime cache when found). If it still does not find, it will download it from server. LRU item is automatically removed after caches are full. Cache becomes full when item count reaches some predefined limit.

I had written this first for j2me apps (and reused it in android project), and used hard references. That worked nicely.. until some large images started coming. Bitmaps were too huge and it pretty soon filled up the heap.

So I thought I would use SoftReference for Android and WeakReference for J2ME implementations (unfortunately CLDC does not support soft reference). When I quickly wrote few test codes, my cache started behaving very weirdly. UI elements almost never found the images from runtime cache... cache was totally pointless!!

Looks like dalvik has some bug. It removes the soft references even when there is plenty of memory. Well, this is not really a bug, because it is quite legal to remove the soft references whenever, there cannot be any guarantee, however, the expected general behavior is that soft references should not be cleared unless there is pressure from heap usage. Dalvik developers seem to be aware of this issue and it still exists in 2.3.4. http://code.google.com/p/android/issues/detail?id=20015

After digging a bit more, I found that when dalvik intends to collect 'some' soft references, in reality it removes all. So basically, either all soft references are collected or nothing is collected during mark and sweep.

Now I am implementing some logic to cache so that it can limit the items by heap usage also. But would be nice to use soft reference in runtime cache, and I would be relaxed knowing whenever heap is running low, images will be cleared from cache automatically.

is user a monkey

I was looking for some method to know about the free heap amount in Android, and I stumbled upon this in ActivityManager class:

boolean isUserAMonkey()
Returns "true" if the user interface is currently being messed with by a monkey.

ActivityManager#isUserAMonkey()

Haha.. and then immediately a recently reported bug of our project flashed... the bug said, someone tried to press refresh 20 times without giving a break!! Yapp.. isUserBeingAMonkey was surely true then :D

Oh.. there was also a report that some of our testers have rotated the screen over 600 times in the same view!! I wish I could call this isUserBeingAMonkey() at that time!!


Anyways, actually Monkey is a program that generates random touches, clicks, gestures etc for application stress testing.