COLT MCANLIS: So here's a common problem
that I'm sure you've run into.
It's time to load a bitmap into memory
for your app, social media stream or whatever,
but you're out of memory.
You can't load in the new bitmap without first
destroying one of the existing bitmaps
you've already got loaded.
But which one do you get rid of?
My name is Colt McAnlis, and the answer to this tricky question
is actually an entire field of computer science
that's almost 60 years old, cache replacement algorithms.
In general, you can think of the bitmaps
that you have loaded into memory as a cache.
That is, rather than loading a given bitmap from disk
each time you want to use it, you load it into memory once
and then use it over and over again as it's needed.
This basically means that a cache stores objects that
might be used in the future.
However, as the cache starts to fill up,
you eventually need to get rid of one of the items
in order to make room for incoming data.
So which one do you get rid of?
Well, since a cache hold objects that
might be used in the future, the obvious answer
is to evict one of the objects that's not needed anymore;
or at worst, wouldn't be used for the longest period of time.
But it's generally impossible to predict
how far in the future information
will be needed again, even when you've
got awesome algorithms like this hanging around
in your code base.
Good news is that there's a simpler way to do this,
and Android has already done the heavy lifting for you.
The LRU cache class maintains a list of items
that you place into it.
As an object is accessed, it's moved to the front of the list
where it will remain until some other item gets accessed,
and then that gets moved to the front instead.
When the cache fills up and it's time
to choose which element to evict to make room
for the incoming objects, the decision here is easy.
Objects that are accessed more frequently
will reside in towards the front of the list,
while less used objects will hang towards the back.
This replacement algorithm is called LRU, or least recently
used, basically saying that the more an object is used now,
the higher chances are that it'll be used in the future,
and it should stay in the cache, which
is perfect for your social media application.
Now chances are that if a thumbnail hasn't
been seen in awhile, then it's probably
been scrolled offscreen and doesn't
need to be in memory anymore.
Now the LRU cache isn't a typical container,
and as such requires a bit of finesse to get working right.
Firstly, the LRU cache manages elements in a key value
relationship.
When you declare the object, you need
to figure out what's the use for key, and what data
you're going to be storing as a value.
For a cache of bitmaps, this may be
as simple as using a filename and a bitmap object.
Secondly, LRU cache is, by definition,
a bounded container.
It's intended to have a maximum amount of size,
so that you don't end up running off into crazy town,
and destroying your application's memory, which
means during initialization you need
to define the size of your cache.
But be careful here.
Making your cache too small means
you'll get a lot of evictions, and end up
with a lot of unneeded bitmap reloads.
Too large of a cache, on the other hand,
exposes you to potential out of memory errors
all over the place.
As such, the best practice means setting your cache size
based upon the amount of memory available to your application.
Now to find this, you can call the getMemoryClass function,
which indicates how hard of a memory limit
your app should impose on itself in order
to let the system work best.
So grab that number and then do some division
by 8 or something, as a starting value.
Over time, you can tweak this number
based upon what your application needs, or better yet,
what device you're running on and its limitations.
Now the next important part is that we
have to let the cache know the size of the objects
that it contains, so it knows how much of its bounded space
is being taken up per object.
To do this, we can simply extend the class
with our own container, override the size of method
to return the size of a given bitmap in bytes.
Now getting objects from the cache
and putting objects back into the cache
are pretty straightforward.
I mean the whole thing works basically like a hash table.
But note that the get function is actually very helpful here.
If you request an object that's not in the cache,
or it's been evicted and doesn't reside there anymore,
then you'll get a null object return,
which is a great signal.
It means that you should do the extra work to load the bitmap
and put it in the cache.
But don't worry.
LRU cache will handle all of the eviction and memory
reclamation for you.
But it's worth pointing out that any time you
insert a new object into the cache,
that some of the existing objects
may be evicted in order to make space for it.
It's also important to note that if your cache objects aren't
uniform in size, then LRU cache could end up
evicting multiple objects in order to make space
for the larger new one.
So, see?
There you go.
Memory management made easy.
But trust me here.
There's plenty more to be done to make your application
faster, which is why you need to check out
the rest of the Android performance pattern content.
And don't forget to join our Google+ community as well,
to ask others how they've solved similar problems.
So keep calm, profile your code, and always remember,
PERFMATTERS.