Handler memory leak problem
In multithreaded operations, handlers are used a lot, but each time you use handlers you are not considering memory leaks.
If you’re doing something with handler, You’ll find the following prompt: This Handler class should be static or leaks might occur (anonymous Android.os.handler). Because this Handler is declared as an internal class, it may prevent the external class from being garbage collected. If the Handler uses Looper or MessageQueue as a thread other than the main thread, there is no problem. If the Handler is using the main thread’s Looper or MessageQueue, then the Handler declaration needs to be fixed as follows: declare Handler as a static class; In the external class, instantiate WeakReference of the external class, and pass this object to Handler when instantiating Handler; Use a WeakReference object to make all references to members of an external class.
Warning Reason: Handler is not set up as a static class, and declaring internal classes may prevent GC collection, resulting in a memory leak
So why does it leak? A Memory Leak refers to a situation in which the dynamically allocated heap Memory of a program is not released or cannot be released for some reason, resulting in a waste of system resources, resulting in slow running of the program or even a system crash. Problem code:
public class MainActivity extends AppCompatActivity { private Handler mHandler = new Handler(); private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = (TextView) findViewById(R.id.tv); Mhandler.postdelayed (new Runnable() {@override public void run() {mTextView.settext (" mTextView.settext "); yiyi") ; }}, 1000); }}Copy the code
Cause of memory leak
In Java, non-static inner classes hold references to external classes. PostDelayed is a delayed processing message that loads a handler into message. MessageQueue gets the message for processing by Looper. If your activity is about to exit and you want to call **destroy** while Looper is processing messages, the life cycle of **Looper** is significantly longer than that of the activity, which makes it impossible for the activity to be collected by the **GC**, resulting in a memory leak. The handler also holds a reference to the activity, which is also a cause (not the root cause) of the memory leak.
For example, TextView is created from an internal class. It also holds references to the external class Activity. The root cause of the memory leak is that it has a shorter lifetime than an Activity. When an Activity is destroyed, it can be reclaimed by the GC
conclusion
When the handler has no or is processing a message, the handler life cycle is significantly longer than that of the Activity. The GC holds references to both the Activity and the handler, so the Activity cannot be reclaimed by the GC, causing a memory leak. Whether a handler is an internal class is not the root cause of the memory leak.
The solution
Static inner class + weak reference
Subclass Handler as a static inner class, and use a WeakReference to hold an Activity instance
Reason: Weakly referenced objects have a short lifetime. The garbage collector collects weakly referenced objects regardless of whether memory is sufficient.
public class HandlerActivity extends AppCompatActivity { private static class MyHandler extends Handler { private final WeakReference< HandlerActivity> mActivity; public MyHandler(HandlerActivity activity) { mActivity = new WeakReference< HandlerActivity> (activity); } @Override public void handleMessage(Message msg) { HandlerActivity activity = mActivity.get(); if (activity ! = null) { } } private final MyHandler mHandler = new MyHandler(this); private static final Runnable mRunnable = new Runnable() { @Override public void run() { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mHandler.postDelayed(mRunnable, 1000 * 60 * 1); finish(); }}Copy the code
At the end of the Activity lifecycle, empty message queue Just in the Activity onDestroy () method call mHandler. RemoveCallbacksAndMessages (null); Will do.
@Override
protected void onDestroy() {
super.onDestroy();
if(handler!=null){
handler.removeCallbacksAndMessages(null);
handler = null;
}
}
Copy the code