You may have read my last post on my techniques in Friendcaster for caching Bitmaps. In that post I argued against using LruCache and instead advocated the use of a thin cache based on SoftReferences. The main reason I argued against LruCache was because of this:
There is a nice callback in the cache called entryRemoved which gives you the item after it’s removed, and you would think here is the perfect time to call Bitmap.recycle(), but do not do this. The reason is that there is no guarantee that the Bitmap isn’t being referenced by a View, and you can’t recycle a Bitmap being used.
For a project I’m working on at the moment, I’m spending a lot of time making the UI as ‘buttery‘ as possible and one way I’m trying to achieve that is with a more concrete caching system, so I decided to revisit LruCache to see if I could fix the problems.
What I come up with is actually very simple.
- First I created a wrapper class for Bitmap which keeps track of whether it’s Bitmap is displayed at a particular time. It also keeps track of whether it is referenced by a cache. If the wrapper is not being displayed or cached, then it calls Bitmap.recycle().
- Then I wrote a ImageView which uses this wrapper object, and sets the ‘being displayed’ flag appropriately.
- Finally I extended LruCache to create BitmapLruCache, which sets the ‘being cached’ flag in put() and entryRemoved().
I could now explain how it works, but it’s probably better if I just point you to the code:
There is a small problem with this method: if entryRemoved() is called on a wrapper object and the bitmap is being displayed, then we can’t do anything with it (it’s just de-referenced). That means that the bitmap will not be explicitly recycled by the cache. If anyone can think of a way to fix this let me know.
This problem has now been fixed (thanks to Jesse Wilson), and the post updated where necessary.