Android background image high memory usage issue

Hi everyone,

In my previous article, I have mentioned that I am doing memory analysis for one of our apps. While investigating memory usage on Motorola Nexus 6 phone, I realized something unusual.

Android Studio's Memory Monitor - 68 MB! Wooww

Android Studio’s Memory Monitor – 68 MB

Android Studio’s Memory Monitor was telling me that our app was using 68 MB memory. The app doesn’t have any large images except the one in the background. I decided to investigate the reason behind it and I finally fixed the issue. In this article, will explain how I tracked down and fixed the issue.

To start with, I used Allocation Tracker of Android Studio’s Memory Monitor. I expanded Thread 1 saw that onCreate() was making an allocation of 49,035,328 bytes (~47 MB). I expanded the call until I find the cause. Looking at the calls, it is obvious that an image is causing this issue.

Result of Allocation Tracking

Result of Allocation Tracking

 

I put break points to onCreate() and stepped into the calls until I reach PhoneWindow.java class. In this class, background image is assigned in this block of code:

if(mBackgroundResource !=0){
    background = getContext().getDrawable(mBackgroundResource);}
else{
    background = mBackgroundDrawable;}

The background Bitmap had a resolution of 2626×4669. However, the background image I used has a resolution of 750×1334

I had two concerns about the size of Bitmap and scaling and posted on Stackoverflow. Android master CommonsWare responded with a clean answer, I will just summarize his answer here.

1) The reason Bitmap is scaled to 2625×4669, even if the phone had a resolution of 1440×2560, is that somehow I put my background image to drawable folder. The drawable folder is same as drawable-mdpi and mdpi stands for 160 DP according to Android.

Motorola Nexus 6 has a density of 560 DP. To match that, Android resized the image multiplying by 560/160 = 3.5

750 x 3.5 = 2625 and
1334 X 3.5 = 4669

2) The reason it was 47 MB is that, even if the image is 160 KB on the disk, Bitmaps are decompressed version of this files and every pixel in ARGB_888 takes 4 Bytes of memory.

2625x4669x4 = 49,024,500 bytes ~ 47 MB

The Solution

Before explaining the solution always remember to test your application on different devices. That was the main reason I missed that point. The device I tested my app during development cycle had 245 DP and it was hard to notice memory leak (It was using only 15 MB memory). Android documentation has a section for Supporting Different Screen Sizes, but it doesn’t mention that upper-scaling of images can cause high memory usage. It would be nice to see that in the documentation.

The solution to that is putting a single image to drawable-nodpi folder or creating images at different resolutions for different densities. To understand the difference between these options you can check CommonsWare‘s article.

To create images for different densities remember to scale your images according to 3:4:6:8:12:16 ratio.

For reference,Supporting Different Screen Sizes

After fixing this issue, the memory usage on Motorola Nexus 6 dropped to 29 MB.

Memory Usage After

Memory Usage After

Android Memory Leak in createFromAsset() Method

Hi guys,

We are about to ship an Android application at my company, and I decided to do some memory analysis before we ship it, for a better performance. I was trying to investigate memory leaks, and one detail grabbed my attention.

You can print your app’s memory allocation typing this command in Terminal:

adb shell dumpsys meminfo <package_name|pid> [-d]

So I put this command and it printed that:

Output of memory analysis

Output of memory analysis 

Nothing seems extraordinary here. When I scroll down I saw that:

Output of memory analysis - Asset Allocations

Output of memory analysis – Asset Allocations

 

Investigating Your RAM Usage page of android gives an example output from Maps application, but they don’t have Asset Allocations section in their example.

In the first output, I counted number of asset allocations and it showed 30 allocations. I continued to use application by closing/opening new activities/fragments and the number was growing. In the example it is 93, but after the screenshot I reached 165 allocations.

The Problem

The problem is that every time I call createFromAsset() it was creating a new asset and keeping a reference to it. I searched the problem online and I found the following Stack Overflow post:  Investigating Android Memory Info.

The answer had a link to the issue reported in Android Open Source Project.

I read the issue and all comments. They claimed that it was fixed in 3.2.4. However, I am using 6.0.0 and seeing the same issue.

The Solution

One user on this page found a work around caching assets and it works fine for now. Until they actually fix it, this is the best we can do I believe:

public class Typefaces {
private static final String TAG = "Typefaces";
private static final Hashtable<String, Typeface> cache = new 
Hashtable<String, Typeface>(); 

    public static Typeface get(Context c, String assetPath) {
    
    synchronized (cache) {
            if (!cache.containsKey(assetPath)) {
                try {
                    Typeface t = Typeface.createFromAsset(c.getAssets(),assetPath);
                    cache.put(assetPath, t);
                } catch (Exception e) {
                Log.e(TAG, “Could not get typeface ‘” + assetPath + "' because " + e.getMessage());

                return null;
                }
            }

            return cache.get(assetPath);
        }
    }
}

In the past, I have seen this problem being reported on Stack Overflow, and I thought it was fixed. Apparently, the bug is still around. There are several blog posts/SO answers about this issue, this post is intended to show how we can catch it without doing deep investigation in Eclipse MAT.