Saturday, November 1, 2014

Reducing memory consumption (Part 2)

As we have seen in earlier post first method to reduce memory was reducing font loading. This is described here.
Next method is to use weak references.

2. Making use of weak references
Our application uses observer model to notify data availability. Here a EventNotifier class is used to register listeners for the event. As soon as the event occurs, it fires the event with EventId and EventObject.

Some activities use data that is fetched from server. In order to get this event activity registers itself with the EventNotifier. There is a communicator thread, which downloads data, and calls method public int eventNotify(int eventType, Object eventObject) of EventNotifier. Now this method finds out registered listeners and informs them about update.

Problem comes when Android decides to kill activity for reusing resources. In this case OnDestroy() method may not get called. OnDestroy() handles unregistration activity from EventNotifier. Now as the EventNotifier still holds reference to this activity, it leaks the large amount of memory required by activity, views shown in it, etc.

This can be resolved by marking the ativity reference as weakreference.

public void registerListener(WeakReference < IEventListener > eventListener, int listenerPriority)

WeakReference allows GC to garbage collect activity instance even if, EventNotifier holds WeakReference of it. With strong reference (created by code "EventListener eventListener = this") GC can not garbage collect the activity instance.

3. Making handlers static and final

When a handler is instantiated inside an activity lifecycle, it holds reference to entire activity. If the activity is destroyed but the handler is still in use, GC can not collect the activity as handler is still holding the reference to it. e.g.

protected void onCreate(Bundle savedInstanceState) {
        ...
    Handler handler = new Handler();
    handler.post(new Runnable() {

        @
        Override
        public void run() {
            // some stuff running on UI thread
        }
    });
        ...
}


Here handler action is created as inner class. Each inner class in Java holds reference to outer class. This handler instance holds instance of Activity which leaks memory. To avoid this make static final instance of handler.

private final MyHandler mHandler = new MyHandler(this);

/**
 * Instances of anonymous classes do not hold an implicit
 * reference to their outer class when they are "static".
 */
private static final Runnable sRunnable = new Runnable() {@
    Override
    public void run() {}
};

@
Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 60 * 10 * 1000);
}

Reducing memory consumption (Part 1)

Recently our app faced lot of issues because of huge memory consumption. We resolved these issues using following techniques:
1. Reducing font instances
2. Making use of weak references
3. Making handlers static and final

In this post we will see first approach. Second approach will be covered in next post.
1. Reducing font instances
We used to have a custom text view. This text view uses a font provided by application. Code below shows how it used to load.

public boolean setCustomFont(Context ctx, String asset) {
    Typeface tf = null;
    try {
        tf = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + asset.trim());
    } catch (Exception e) {
        Log.e(TAG, "Could not get typeface: " + e.getMessage() + "\n value of asset string " + asset);
        return false;
    }

    setTypeface(tf);
    return true;
}

After using memory profiling tool of android, we came to know that each time this activity is opened memory usage increases. This tool shown the resource held as well.
adb shell dumpsys meminfo com.example.prj
Next step was to reduce the repeated loading of font.
private static Typeface typeFace = null;
public static Typeface getTypeFace(Context ctx, String asset) {
    if (typeFace == null)
        typeFace = Typeface.createFromAsset(ctx.getAssets(), "fonts/" + asset.trim());
    return typeFace;
}
Code above makes the object of type face only once. If it already exists it won't be created again and old object will be used. Before making this change, whenever activity using this TextView was opened, it used to increase memory consumption by 4-5Mb. The size of font file was 0.5 Mb and it was getting loaded 8-10 times on this activity. After making this change, font is loaded only once and takes about 0.5Mb memory.

Android aar deployment in Maven - 2022

Introduction If you are working on android library project, you might be wondering how to publish it on Maven like this . Earl...