If you’re an experienced Android developer, you more than likely have a wealth of experience of OutOfMemoryExceptions (OOM) under your belt too!
In this post, I’m going to be looking at Android memory management and will be highlighting key areas that can help to reduce your app’s memory usage – especially where images are concerned.
How does memory management work for Android?
Firstly, we need to make sure that we know how both Android and Dalvik virtual machines work. If you’ve used them during the development process before, you’ll know that there’s a mechanism that runs once it has been determined that a piece of memory hasn’t been used. This is referred to as the ‘garbage collection’. The goal of said garbage collection is to free up memory that isn’t being used in order to allocate it to other resources that need it.
We also need to ensure that we know how to handle heap sizes. Every app on your smartphone has a limited heap size. It starts small before gradually growing until it reaches its upper limit.
The heap size, however, is not always the same as it depends on the amount of available RAM. You therefore may find that on some devices your app has a much bigger space to allocate its resources and that it’s thus able to consume more and more memory without crashing. On other devices, however, it may cause OutOfMemory crashes.
Luckily, there are some tools that you can use to check the amount of memory that your app is using – allowing you to make quick changes to the things that cause it to reach its peak. More on that later!
What is causing these memory leaks?
Once you know the basics of memory management, the solution is simple…right?! Not quite…There are, unfortunately, a lot of things that can cause memory leaks in your app. There are a number of things you can do, however, to avoid them.
For example, you could start by ensuring that you have got rid of any variables that you’ve declared in your code but won’t actually use. If you’ve initialised them with data, removing them will help to allocate more memory!
You also need to remember to not create new variables inside a loop. Declare your variables before starting loops to avoid creating new ones that allocate their space at every step. New variables can sometimes be necessary, sure, but you should try and avoid using them for the most part. It’s also important to check your code for any possible issues relating to any external libraries that you’re using. If you don’t know what they do internally, you might find some surprises that you’re not going to like! Popular libraries are less likely to be badly implemented but you should always be aware of the potential risks.
These are just some of the things that can cause memory leak issues in your app. The Android Developers website offers plenty of performance tips concerning optimisation but, in this next section, I’m going to be specifically looking at handling images.
How can I handle images properly?
Images and bitmaps are probably the biggest culprits for causing OOM exceptions in Android apps, so you have to approach them with caution.
Imagine we’re working on an image gallery app that retrieves images from a server. The server shares images to the app, of unknown size, and it’s our job to sort these images into a grid. How do we create this app without having to deal with multiple memory issues?
Firstly, you need to try and retrieve these images from the server alongside their correct size. If you receive a 300×200 resolution image, you’re going to have a lot of numbers to deal with. There’s no need to panic, however, if your server is always serving up 4K resolution images! Android has a way of handling that.
You will need to set a property called inJustDecodeBounds in BitmapFactory before creating the bitmap. Once you’ve set the property to ‘True’, the memory won’t be allocated but you will have information on the width, height and type of image that you’re going to receive. You can then create a scaled version of the bitmap – allocating the resources needed before setting it in your ImageView. You can find the code online to help you achieve that particular outcome if you’re interested.
When you create a bitmap, you will find that you have to set the Bitmap.Config. This parameter sets how many pixels will be stored, resulting in either less or more quality depending on the configuration you choose. This configuration has a direct impact on the memory that will be allocated for this image as when we load a bitmap from memory it gets decompressed. This is when the memory issues can occur.
The amount of memory that we see in a file image in our disk of, for example, 300Kb is not the same amount of memory that will then be allocated once this image gets decompressed and loaded. In this instance, it can easily allocate 20MB of RAM.
Here are some examples of the amount of memory that an image of 3840 x 2160 pixels would allocate when decompressed using different configurations:
This configuration means that you have four channels – Alpha, Red, Green, Blue – and each one offers you a Byte (8 bits) to store the pixel information.
3840 * 2160 * 4 bytes → 32.4 MB
This configuration means that you again will have four channels – Alpha, Red, Green, Blue – but each one will have half a Byte (4 bits) to store the pixel information.
3840 * 2160 * 2 bytes → 16.2 MB
This configuration means that you will have three channels – Red, Green, Blue. The images that display no transparency are the ones that have no alpha channel. The pixel information of the image will be stored in 2 bytes, meaning 5 bits for Red, 6 bits for Green and 5 for Blue.
3840 * 2160 * 2 bytes → 16.2 MB
As you can see, the memory allocation that takes place varies with different configurations. If your image does not have alpha, I would recommend that you use the RGB_565 config. It will give you better quality than ARGB_4444 because it’s using the bits of the alpha channel to store more data for colours. If your image has alpha, go for ARGB_4444. In normal circumstances with these configurations, you’re not going to see bad quality images. If you need high quality images and you can’t afford to lose pixels, go for ARGB_8888. This configuration, however, can cause more OutOfMemory issues.
If you need some help loading images more efficiently, I recommend that you refer to either the Picasso or Glide libraries. You could also check for app memory leaks with the Leak Canary library.
These aforementioned libraries will make your life a lot easier! I would also recommend that you take a look at the powerful Android Studio Profiler tool which will help you find out where your resources are being lost.
Once you’ve followed the aforementioned steps, you’ll be well on your way to creating Android apps that experience fewer OOM exceptions and, in turn, you’ll be able to offer a much more engaging user experience too!
In the efforts of preventing this blog post from becoming ridiculously long, it offers a mere overview of how memory management works using Android. Please refer to the official documentation for a much more thorough version.
Are you following us on Twitter, LinkedIn and Medium yet? Follow us to be notified of our latest posts and to receive exclusive content.
Looking for something to read? We’ve got a nice selection of blog posts for you to choose from! Some of our latest posts include Designing for Android and How Minimalism Can Help You Achieve The Perfect Work-Life Balance.
(image credit: pcmag)