Image & Memory allocations in Android applications
If you have ever worked with lots of images in Android , you just know what we are talking about here. So lets jump to the thoughts right way.
There are 3 kind of heap memory in Android. -
1. Java Heap Memory
2. Native Heap Memory
and another one. We will talk about it later.
A main difference between these 2 types are - java heap memory is auto garbage collected but there is no garbage collection in native heap memory. And java heap memory is mainly used for java objects and obviously native heap memory for C++ objects.
Now for image loading lets do some simple math for image data. Each pixel takes up 4 bytes of data — one byte for each red, green, blue, and alpha. If a phone has a screen size of 720 x 1280 pixels (like Moto G), a single full-screen image may take up 3.5 MB of memory. Its a lot of memory when you are loading images in list view or something. Presumably "Allocation-Claiming-Reclaiming" stuff in java heap memory for list of images can be a heavy task.
So how can we improve this thing. Obviously there are many efficient image loading libraries available in open source community. We might be already using unintentionally what we are going to say but still might come handy to know-
Now the third kind of memory is the "ashmem". Something like following thing happens there -
Android "staple" a memory when it is allocated. But when this allocated memory is no longer required, it is not cleared/freed up. Rather android "un-staple" it (kind of ready for freeing state). This memory is freed only if the system actually needs more memory in future. So when android wants to reclaim the memory again (e.g. loading the same image), old data might still be there if it has not been cleared up.
Purgeable bitmaps
Ashmem is not directly available to Java app, but images are one of the few exceptions. When we create bitmap, the android allows you to specify that the image be purgeable:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeByteArray(jpegByteArray, 0,jpegByteArray.length, options);
Purgeable bitmaps live in ashmem. However, the GC does not automatically reclaim them. Android's system libraries “staple” the memory when the view system is rendering the image, and “unstaple” it when it's finished. Memory that is unstapled can be reclaimed by the system at any time. If an unstapled image ever needs to be drawn again, system will just decode it again, on the fly.
But the catch is - this on-the-fly decoding is a CPU intensive operation runs on the UI thread so there is a high chance that app might stall during the operation. So it was deprecated from Api level 21. Now it is advised to use inBitmap flag. Being introduced in Android 3.0, it only worked well for uniform image size which was good enough for many use cases but in real life we can't really have that much control if images are uploaded by our users. The good news is from Android 4.4 , that limitation is handled too.
Lets hope from now on we will not only write
options.inBitmap = true;
cause someone said so, but because of we know so ;)
Kindly share your thoughts and other improvements in comments which might be interesting to know.