Android Memory Leaks

Let's Get The Job Done!

Android Memory Leaks

memory leaks

Here i’m gonna discuss the memory leak and some causes of this defect in android apps.

So what is the “Memory Leak”?

Simply the memory leak means that you have allocated a memory to an object but never have the chance to free up that allocated memory. that causes the garbage collector neglect that part of memory to take out and reuse later.

Ignoring some trivial bytes of memory to be freed is not important but imagine you have not recycle memory for thousand of iterations, then you will end up in a memory that is sinking down!.

Just to remind you, the most wide-spread tool to detect memory leaks is Leak Canary library.

So here is the most common causes of memory leaks in android apps:

Broadcast Receivers : when you register a broadcast receiver but never call unregister at the activity exit, you will definitely end up in a memory leak. keep in mind that better to call the register method on onStart() and unregister method on onStop().

Static Activity/View Reference: When you declare a View or Activity as Static then when you reference that object directly or indirectly, the object would not be garbage collected and you will end up in memory leak, so keep in mind that never declare an activity or view as static.

public class SomeClass  {
    
    // correct
    public View mViewCorrect ; 
    public MainActivity mMainActivityCorrect; 
    
    
    // not correct, will end up into memorty leak
    public static View mViewNotCorrect ;
    public static MainActivity mMainActivityNotCorrect ;

}

Singleton class reference: If you pass an object like Context into your singleton then you will end up in a memory leak, and interestingly seems that it is a common mistake.

Here I am adding a Context object into the singleton and memory leak is inevitable.

public class ThreadSafeSingleton {
    
    private static ThreadSafeSingleton instance;

    private ThreadSafeSingleton(Context context){}

    public static synchronized ThreadSafeSingleton getInstance(Context context){
        if(instance == null){
            instance = new ThreadSafeSingleton(context);
        }
        return instance;
    }

}

So how to overcome this issue, first off you could replace the Context object with ApplicationContext, but the general solution is to make sure that your injected object is null when there is no need for singleton object.

Here I’ve added a cleanUp() method and will call it whenever I am sure I have no need for singleton object, for example on the onStop() method of activity.

public class ThreadSafeSingleton {

    private static ThreadSafeSingleton instance;
    private static Context mContext ; 

    private ThreadSafeSingleton(Context context){}

    public static synchronized ThreadSafeSingleton getInstance(Context context){
        if(instance == null){
            instance = new ThreadSafeSingleton(context);
            mContext = context ; 
        }
        return instance;
    }
    
    
    public void cleanUp(){
        mContext = null ; 
    }

}

 

Inner class reference: When you define an inner class for example an AsyncTask, the inner class always need a reference to the outer class and inevitably the memory leak will occur after activity destroyed. so how we could fix this, simply by not defining an inner class!. another fix is to define the inner class as static as instances of anonymous classes don’t hold any reference to the parent class.

public class InnerClassMemoryLeaks {

    // avoiding memory leak
    public static class SuspiciousInnerClass{

        public void doSomeOps(View view){

        }
    }
}
public class MemoryLeaksUsages {
    InnerClassMemoryLeaks parent = new InnerClassMemoryLeaks() ;

    public void doOps(){

        InnerClassMemoryLeaks.SuspiciousInnerClass innerClass
                 = new InnerClassMemoryLeaks.SuspiciousInnerClass() ;

        View.OnClickListener clickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                innerClass.doSomeOps(v);
            }
        } ;
    }
}

 

Anonymous class reference: As similar as above when you define an anonymous class you will end up in a memory leak, to fix this the best option is to cancel the object operation or set it null on activity exit. for example when you create an AsyncTask in your activity make sure you’ve canceled operation on onStop() method of the activity.

void someAsyncTask(){
    async =  new AsyncTask<Void, Void, Void>(){
        @Override protected Void doInBackground(Void...params){
            while(true);
        }
    }.execute();
}

Then:

public class SomeActivity extends AppCompatActivity{

    //code here



   // desctroying the asyncTask
    @Override
    protected void onDestroy() {
        super.onDestroy();
        async.cencal(true) ;
    }
}

 

Handlers: This is another common mistake we do as android developers, when you create a handler and pass an anonymous Runnable into it, you will end up in a memory leak, because when a Handler is instantiated on the main thread, it is associated with the Looper’s message queue. Messages posted to the message queue will hold a reference to the Handler, to fix this, one solution is to create an inner class extending Handler inside the activity or fragment and hold a weak reference of that activity/fragment into that.

The solution is described in a Stack Overflow post.

public class MyGridFragment extends Fragment{

    static class MyInnerHandler extends Handler{
        WeakReference<MyGridFragment> mFrag;

        MyInnerHandler(MyGridFragment aFragment) {
            mFrag = new WeakReference<MyGridFragment>(aFragment);
        }

        @Override
        public void handleMessage(Message message) {
            MyGridFragment theFrag = mFrag.get();
            switch (message.what) {
            case 2:
                ArrayList<HashMap<String,String>> theurls = (ArrayList<HashMap<String,String>>) message.obj;
                theFrag.urls.addAll(theurls);
                theFrag.theimageAdapter.notifyDataSetChanged();
                theFrag.dismissBusyDialog();
                break;
            }//end switch
        }
    }
    MyInnerHandler myHandler = new MyInnerHandler(this);
}

 

Threads: The same rule as above is happening to threads you’ve defined in your activity, the simple work-around is to close/destroy your thread in the onStop() or onDestroy() methods of your activity or fragment.

 

TimerTask: Again when you define a TimerTask in your activity, this object is going to hold a reference to your activity and will eventually end up into a memory leak, the easy work-around is to cancel/close the TimerTask object on the onStop() or onDestroy() methods of your activity or fragment.

 

that’s it, hope you enjoy! and feel free to share.

 

 

references :

                     https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

                     https://blog.nimbledroid.com/2016/05/23/memory-leaks.html