SoFunction
Updated on 2025-03-04

The ultimate solution to Android memory leaks (Part 2)

1. Overview

existThe ultimate solution to Android memory leaks (Part 1)In this article, we introduce how to check whether an app has memory leaks. This article will summarize the code of typical memory leaks and give corresponding solutions. The main problems of memory leaks can be divided into the following types:

  • Memory leak caused by static variables
  • Memory leak caused by non-static internal classes
  • Memory leak caused by resource not shutting down

2. Memory leak caused by static variables

In java, the life cycle of static variables starts when the class is loaded and ends when the class is unloaded. In other words, in android its life cycle starts when the process starts and ends when the process dies. Therefore, during the run of the program, if the process is not killed, the static variable will always exist and will not be recycled. If a static variable strongly refers to a variable in an Activity, then the Activity will not be released, even if the Activity executes onDestroy (do not equalize the execution of onDestroy with recycled). The solution to this type of problem is: 1. Find a replacement object that is similar to the life cycle of the static variable. 2. If it cannot be found, change the strong quotation method to weak quotation. A relatively typical example is as follows:

Context memory leak caused by singleton

public class IMManager {
  private Context context;
  private static IMManager mInstance;

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

  private IMManager(Context context) {
     = context;
  }

}

When getInstance is called, if the passed context is the context of the Activity. As long as this singleton is not released, the activity will not be released.

Solution
Pass the context of Application. Because the life cycle of the Application context is longer than the Activity, it can be understood that the life cycle of the Application context is as long as the life cycle of a singleton, and passing it into it is the most suitable.

public class IMManager {
  private Context context;
  private static IMManager mInstance;

  public static IMManager getInstance(Context context) {
    if (mInstance == null) {
      synchronized () {
        if (mInstance == null)
          //Convert the passed context into the Application context          mInstance = new IMManager(());
      }
    }
    return mInstance;
  }

  private IMManager(Context context) {
     = context;
  }

}

3. Memory leak caused by non-static internal classes

In java, creating a non-static inner class instance will reference its peripheral instance. If this non-static internal class instance does some time-consuming operations, the peripheral object will not be recycled, resulting in memory leaks. The solution to this type of problem is: 1. Turn the inner class into a static inner class 2. If there is a strong reference to a property in the Activity, change the reference method of the property to a weak reference. 3. When the business allows, these time-consuming tasks are ended when the Activity executes onDestory.

Memory leak caused by internal threads

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.aty_leak);
    test();
  }

  public void test() {
    //Anonymous internal class will refer to its peripheral instance, so it will cause memory leaks    new Thread(new Runnable() {

      @Override
      public void run() {
        while (true) {
          try {
            (1000);
          } catch (InterruptedException e) {
            ();
          }
        }
      }
    }).start();
  }
  }

Solution
Modify non-static anonymous internal classes to static anonymous internal classes

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.aty_leak);
    test();
  }
  // Add static to become static anonymous internal class  public static void test() {
    new Thread(new Runnable() {

      @Override
      public void run() {
        while (true) {
          try {
            (1000);
          } catch (InterruptedException e) {
            ();
          }
        }
      }
    }).start();
  }
}

Memory leak caused by Handler

public class LeakAty extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.aty_leak);
    fetchData();

  }

  private Handler mHandler = new Handler() {
    public void handleMessage( msg) {
      switch () {
      case 0:
        // Refresh data        break;
      default:
        break;
      }

    };
  };

  private void fetchData() {
    //Get data    (0);
  }
}

mHandler is an anonymous internal class instance and will refer to peripheral objects. If the Handler still has messages to process when the Activity exits, then this Activity will not be recycled.

Solution

public class LeakAty extends Activity {
  private TextView tvResult;
  private MyHandler handler;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    (savedInstanceState);
    setContentView(.aty_leak);
    tvResult = (TextView) findViewById();
    handler = new MyHandler(this);
    fetchData();

  }
  //The first step is to change the Handler to a static inner class.  private static class MyHandler extends Handler {
    //Second step, change the places where the Activity needs to be referenced into a weak reference.    private WeakReference<LeakAty> atyInstance;
    public MyHandler(LeakAty aty) {
       = new WeakReference<LeakAty>(aty);
    }

    @Override
    public void handleMessage(Message msg) {
      (msg);
      LeakAty aty = atyInstance == null ? null : ();
      //If the Activity is released and recycled, these messages will not be processed      if (aty == null||()) {
        return;
      }
      ("fetch data success");
    }
  }

  private void fetchData() {
    // Get data    (0);
  }

  @Override
  protected void onDestroy() {
    //The third step is to remove the callback when the Activity exits    ();
    (null);
  }
}

4. Memory leak caused by unclosed resources

When BraodcastReceiver, Cursor, Bitmap and other resources are used, they need to be released in time when they are not needed. If they are not released, memory leaks will occur.

To sum up, the main situation of memory leaks is the above three types, which ultimately boils down to one point, that is, the resources are not released when they are not needed. Therefore, during the encoding process, you should pay attention to these details to improve the performance of the program.