Skip to content

Sob story about Memory Leak in Android

Published: at 8 min read

Table of content

what is memory leak?

When the memory usage grows continuously over time of an Android app, because it fails to release or de-allocate memory that is no longer needed. This can lead to a variety of problems, including ~

  1. sluggish performance
  2. reduced responsiveness
  3. in severe cases, app crashes or the termination of your app by the Android system.

Memory leaks can happen for various reasons in Android apps, and result of resources not being released properly.

Common causes of memory leaks in Android:

Reasons of Memory Leak

  1. Broadcast Receiver - can contribute to memory leaks in Android if they are not registered and unregistered properly.
    • When a Broadcast Receiver is registered dynamically (i.e., using registerReceiver()), it needs to be unregistered to avoid holding references to the context indefinitely.
    • If a receiver is not unregistered, it can lead to memory leaks, as the system won’t be able to release the associated resources.

Example :

public class MainReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // Handle the broadcast message
    }
}

public class MainActivity extends AppCompatActivity {
    private MainReceiver mainReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mainReceiver = new MainReceiver();
        registerReceiver(mainReceiver, new IntentFilter("my_action"));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // Memory leak: The receiver should be unregistered in onDestroy
        // unregisterReceiver(myReceiver);
    }
}
@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(myReceiver);
}
  1. Static references to activities or views — in Android can lead to memory leaks if

Example :

public class ReferenceActivity extends AppCompatActivity {

    /*  
     * This is a bad idea! 
     */
    private static TextView textView;
    private static Activity activity;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);
        
        
        textView = findViewById(R.id.activity_text);
        textView.setText("Bad way of coding!");
           
        activity = this;
    }
}
  1. Singleton Class Reference
public class MSingleton {
    private static MainActivity sCurrentActivity;

    public static void setCurrentActivity(MyActivity activity) {
        sCurrentActivity = activity;
    }

    public static MainActivity getCurrentActivity() {
        return sCurrentActivity;
    }
}

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Setting the current activity in the singleton
        MSingleton.setCurrentActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // Do not rely on garbage collection to clear static references
        // This can lead to a memory leak
        // MSingleton.setCurrentActivity(null);
    }
}
  1. MSingleton is a class with a static reference sCurrentActivity to an activity (MainActivity).
  2. In MainActivity, during the onCreate method, the current instance of the activity is set as the current activity in the singleton using MSingleton.setCurrentActivity(this).
  3. If the onDestroy method of the activity is not properly handling the removal of the activity reference from the singleton (e.g., by calling MSingleton.setCurrentActivity(null)), a memory leak can occur.

To Solve this issue — when using static references, it’s crucial to manage those references properly.

@Override
protected void onDestroy() {
    super.onDestroy();

    // Properly clear the static reference to avoid memory leak
    MSingleton.setCurrentActivity(null);
}
  1. Timer Task — Using TimerTask in Android can potentially lead to memory leaks if

Example :

import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {

    private Timer timer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Creating a TimerTask that holds a reference to the Activity
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // Some task that refers to the Activity
                // For example, trying to access a view in the Activity
                TextView textView = findViewById(R.id.textView);
                // This reference can cause a memory leak
            }
        };

        // Creating a Timer that schedules the TimerTask to run periodically
        timer = new Timer();
        timer.schedule(timerTask, 0, 1000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // Cancel the Timer to prevent memory leak
        if (timer != null) {
            timer.cancel();
        }
    }
}
@Override
protected void onDestroy() {
    super.onDestroy();

    // Cancel the Timer to prevent memory leak
    if (timer != null) {
        timer.cancel();
        timer.purge(); // Purge the cancelled tasks from the timer's task queue
    }
}
  1. Threads Reference — can potentially lead to memory leaks if

Example :

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Creating a new thread with an anonymous inner class
        Thread mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                // Some task that refers to the Activity
                // For example, trying to access a view in the Activity
                TextView textView = findViewById(R.id.textView);
                // This reference can cause a memory leak
            }
        });

        // Starting the thread
        mThread.start();
    }
}

To avoid this issue we can follow the below two ways :

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final WeakReference<Activity> weakActivity = new WeakReference<>(this);

        // Creating a new thread with an anonymous inner class
        Thread mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                Activity activity = weakActivity.get();
                if (activity != null) {
                    // Some task that refers to the Activity
                    // For example, trying to access a view in the Activity
                    TextView textView = activity.findViewById(R.id.textView);
                    // Safely use the activity reference
                }
            }
        });

        // Starting the thread
        mThread.start();
    }
}
public class MainActivity extends AppCompatActivity {

    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Posting a task to the main thread using a Handler
        handler.post(new Runnable() {
            @Override
            public void run() {
                // Some task that refers to the Activity
                // For example, trying to access a view in the Activity
                TextView textView = findViewById(R.id.textView);
                // Safely use the activity reference
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // Remove any remaining callbacks to prevent memory leaks
        handler.removeCallbacksAndMessages(null);
    }

}

So at the end we can say to avoid memory leak we should —

By following these best practices and using appropriate debugging and profiling tools, you can help ensure that your Android app’s memory usage is efficient and does not suffer from memory leaks.

Happy learning !

Share :
Written by:Parita Dey

Interested in Writing Blogs, showcase yourself ?

If you're passionate about technology and have insights to share, we'd love to hear from you! Fill out the form below to express your interest in writing technical blogs for us.

If you notice any issues in this blog post or have suggestions, please contact the author directly or send an email to hi@asdevs.dev.