Memory leakage caused by Handler in Android
In common Android programming, Handler is often used to perform asynchronous operations and process returned results. Our code is usually implemented in this way.
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } }}
However, the above Code may cause memory leakage. When you use the Android lint tool, you will receive such a warning.
This Handler class should be static or leaks might occur (com.example.multifragment.SampleActivity.1)Issue: Ensures that Handler classes do not hold on to a reference to an outer classId: HandlerLeakSince this Handler is declared as an inner class, it may prevent the outer class from being garbage collected.If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class; In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.
We can see that there may still be some unclear information, where may cause memory leakage in the code, and how can it cause memory leakage? Let's take a closer look.
1. When an Android Application is started, a logoff instance that is used by the main thread is automatically created. Logoff processes message objects in message queues one by one. In Android, all Android framework events (such as Activity life cycle method calls and button clicks) are put into the message and then added to the Message Queue to be processed by logoff, logoff is responsible for processing data one by one. The logoff lifecycle in the main thread is as long as that of the current application.
2. when a Handler is initialized in the main thread, we send a message whose target is the Handler to The logoff message queue, in fact, a sent Message contains a reference of a Handler instance. Only in this way can logoff call Handler # handleMessage (Message) to complete correct Message processing when processing the Message.
3. in Java, non-static internal classes and anonymous internal classes both implicitly hold references to their external classes. Static internal classes do not hold external class references.
Indeed, the above sample code is a little hard to detect memory leaks, so the following example is very obvious.
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Post a message and delay its execution for 10 minutes. mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { /* ... */ } }, 1000 * 60 * 10); // Go back to the previous Activity. finish(); }}
Analyze the code above. When we execute the Activity finish method, delayed messages will exist in the main thread message queue for 10 minutes before being processed, this message contains the reference of Handler, while Handler is an anonymous internal class instance, which holds the reference of SampleActivity outside, so SampleActivity cannot be recycled, as a result, many resources held by SampleActivity cannot be recycled. This is what we often call Memory leakage.
Note that the new Runnable mentioned above is implemented by an anonymous internal class. It also holds a reference to SampleActivity and prevents SampleActivity from being recycled.
To solve this problem, the idea is that non-static internal classes are not applicable. When Handler is inherited, they are either stored in a separate class file or static internal classes. Because static internal classes do not hold external class references, they do not cause memory leakage of external class instances. When you need to call an external Activity in a static internal class, we can use weak references for processing. In addition, you also need to set Runnable to static member attributes. Note: A static anonymous internal class instance does not hold external class references. The modified code will not cause memory leakage:
public class SampleActivity extends Activity { /** * Instances of static inner classes do not hold an implicit * reference to their outer class. */ private static class MyHandler extends Handler { private final WeakReference
mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference
(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } 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, 1000 * 60 * 10); // Go back to the previous Activity. finish(); }}
In fact, many memory leaks in Android are caused by the use of non-static internal classes in the Activity, as mentioned in this Article, therefore, when using non-static internal classes, pay special attention to them. If the life cycle of the objects held by the instance is greater than that of the external class objects, memory leakage may occur. I personally prefer to use static classes and weak references to solve this problem.