Summary of ways to avoid memory leaks in Android programming

  • 2020-06-03 08:21:16
  • OfStack

Android applications are limited to a maximum of 16m, at least on ES3en-ES4en G1. It consists of the phone itself occupied and the developer can use two parts. Even if you don't intend to use all your memory, you should use as little as possible so that other applications don't shut down your application while it's running. The more apps Android can hold in memory, the faster users can switch apps. As part of my work, I have taken a close look at memory leaks in Android applications, most of which are caused by the same error that keeps a reference to a context for a long time (Context).

In Android, context (Context) is used for many operations, but mostly for loading and accessing resources. This is that all widget take 1 context (Context) argument in their constructor. In a qualified Android application, you can usually use two contexts (Context) : activity (Activity) and application (Application). An activity (Activity) is usually passed to a class or method that requires a context (Context) parameter:


@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
 
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
 
  setContentView(label);
}

This means that View has 1 reference to the entire activity (Activity) and maintains references to all objects held in this activity (Activity); Typically they include the entire View hierarchy and all of its resources. Therefore, if you "leak" the context (Context) (here "leak" means that you keep a reference and organize GC to collect it), you will cause a large memory leak. Divulging the entire event (Activity) is a very easy thing to do if you're not careful.

When the direction of the screen changes, the system destroys the current activity (Activity) by default and creates a new one and maintains its state. The result is that Android reloads the application's UI from the resource. Now imagine 1, you write an application, you have a very large bitmap, and you don't want to reload every time you rotate. The easiest way to keep it and not reload each rotation is to save it on a static field:


private static Drawable sBackground;
 
@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
 
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
 
  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);
 
  setContentView(label);
}

This code is very fast, but also very wrong. It reveals the first activity created when the first screen Angle changes (Activity). When an Drawable is attached to an View, this View is set to a callback to drawable. In the code snippet above, this means that this Drawable has a reference to TextView, while this TextView keeps a reference to Activity (the Context object) and this Activity has references to many objects (it depends more or less on your code).

This example is one of the simplest causes of the Context leak. You can see that we solved this problem in the home screen source code (see unbindDrawables() method) by setting the saved Drawable callback to null when Activity was destroyed. More interestingly, you can create a leaked context chain, which of course is very bad. They allow you to run out of memory very quickly.

There are two simple ways to avoid memory leaks associated with context. The most obvious one is to avoid using context outside of its own scope. The above example shows how a static reference inside a class and their indirect references to an external class can be very dangerous. The second solution is to use Application Context. This context exists with your application and does not depend on the life cycle of Activity. If you plan to keep a long life cycle object that requires context, remember to consider Application objects. You can easily get it by calling Context.getApplicationContext () or Activity.getApplication ().

In summary, to avoid memory leaks involving the context, keep the following points in mind:

1. Do not maintain long life cycle references to 1 Activity Context (1 reference to Activity should have the same life cycle as Activity itself)
2. Try using application context (ES83en-ES84en) instead of active context (ES85en-ES86en)
3. If you cannot control their lifecycle, avoid using non-static inner classes in activities (Activity), use static classes, and use weak references to activities (Activity). The solution to this problem is to use a static inner class with a weak reference (WeakReference) external class. Just like ViewRoot and its W inner class.
Garbage collectors are not 100% safe from memory leaks.


Related articles: