preface
- in
Android
Memory leaks are common in development
- Definition of a memory leak: an object that should have been collected cannot be collected and remains in heap memory
- Memory leaks occur when an object is no longer in use and should be recycled but cannot be recycled because another object in use holds a reference to it. This leads to a memory leak.
- This article will detail one of the memory leak scenarios: in
Handler
A memory leak occurred in
Android Development: The Handler asynchronous communication mechanism (including Looper and Message Queue)
directory
1. Problem description
Handler
Common usage = newHandler
Subclass (inner class), anonymousHandler
The inner class
Public class MainActivity extends appActivity {public static final String TAG ="carson:"; private Handler showhandler; @override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); Handler(); MessageQueue showhandler = new FHandler(); // 2. Start child thread 1 newThread() {
@Override
public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // a. Define the Message to be sent Message MSG = message.obtain (); msg.what = 1; // Message identifier msg.obj ="AA"; Send showhandler.sendMessage(MSG) to MessageQueue; } }.start(); // 3. Start child thread 2 newThread() {
@Override
public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // a. Define the Message to be sent Message MSG = message.obtain (); msg.what = 2; // Message identifier msg.obj ="BB"; Send showhandler.sendMessage(MSG) to MessageQueue; } }.start(); } // Analysis 1: Class FHandler extends Handler {// To Override handlerMessage() to determine the operation that updates the UI handleMessage(Message msg) { switch (msg.what) {case 1:
Log.d(TAG, "Message received from thread 1");
break;
case 2:
Log.d(TAG, "Message received from thread 2");
break; }}}} /** * Method 2: Anonymous Handler internal class */ public class MainActivity extends AppCompatActivity {public static final String TAG ="carson:"; private Handler showhandler; @override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); Handler class object instantiated by anonymous inner class // Note: Looper is not specified here, so automatically bind the current thread (main thread) Looper, MessageQueue showhandler = newHandler@override public void handleMessage(Message MSG){switch (MSG. What){case 1:
Log.d(TAG, "Message received from thread 1");
break;
case 2:
Log.d(TAG, "Message received from thread 2");
break; }}}; // 2. Start child thread 1 newThread() {
@Override
public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // a. Define the Message to be sent Message MSG = message.obtain (); msg.what = 1; // Message identifier msg.obj ="AA"; Send showhandler.sendMessage(MSG) to MessageQueue; } }.start(); // 3. Start child thread 2 newThread() {
@Override
public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // a. Define the Message to be sent Message MSG = message.obtain (); msg.what = 2; // Message identifier msg.obj ="BB"; Send showhandler.sendMessage(MSG) to MessageQueue; } }.start(); }}Copy the code
-
The test results
-
The above example will run successfully, but the code will have a serious warning:
- Reason for warning = this
Handler
Class because it is not set to static, soThis caused a memory leak- The final memory leak occurs when
Handler
External class of class:MainActivity
class
So why does this Handler leak memory when it is not set to static?
2. Explain the reasons
2.1 Reserve knowledge
- The main thread of the
Looper
Object life cycle = life cycle of the application - in
Java
,Non-static inner class & Anonymous inner classAll hold references to external classes by default
2.2 Description of leakage causes
You can see from the sample code above:
- The above
Handler
The message queue of the instance has two messages (delay, respectively) from thread 1 and thread 21s
,6s
) - in
Handler
Message queue There are unprocessed messages/messages in the message queue while being processedMessage
holdHandler
Instance reference - Due to the
Handler
= non-static inner class/anonymous inner class (2 ways to use), so it also holds references to external classes by default (i.eMainActivity
Example), the reference relationship is shown in the following figure
The above reference relationship remains until all messages in the Handler message queue have been processed
- in
Handler
The external class should be destroyed if there are unprocessed messages in the message queue/while messages are being processedMainActivity
, but because of the above reference, the garbage collector(GC)
Unable to recycleMainActivity
, causing a memory leak. The diagram below:
2.3 summarize
- when
Handler
There are unprocessed messages in the message queue/there are references to messages being processed:“Unprocessed/processed message ->Handler
Instance -> External Class - If appear
Handler
> the life cycle of the external classnamelyHandler
The message queue has unprocessed messages/processing messages and the external class needs to be destroyed) will make external classes unavailable to the garbage collector(GC)
Reclaim, thus causing a memory leak
3. Solutions
As can be seen from the above, there are two key conditions that cause memory leaks:
- Unprocessed/Processed message -> exists
Handler
Instance -> External class reference relationship Handler
> life cycle of the external class
That is, the Handler message queue has unprocessed messages/processing messages and the external class needs to be destroyed
Solution idea = make any condition 1 above not true.
Solution 1: Static inner class + weak reference
-
Principle Static inner classes do not hold references to external classes by default, so that the “unprocessed/processed message -> Handler instance -> external class” reference relationship does not exist.
-
The concrete scenario sets the subclasses of Handler to static inner classes
- At the same time, you can also use WeakReference to hold an Activity instance
- Reason: Weakly referenced objects have a short lifetime. When the garbage collector thread scans, once it finds an object with only weak references, it reclaims its memory regardless of whether the current memory space is sufficient or not
- To solve the code
public class MainActivity extends AppCompatActivity {
public static final String TAG = "carson:"; private Handler showhandler; @override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);setContentView(R.layout.activity_main); //1. Instantiate the custom Handler class object ->> Analyze 1. Looper is not specified here, so automatically bind Looper and MessageQueue of the current thread (main thread). Showhandler = new FHandler(this); showhandler = new FHandler(this); // 2. Start child thread 1 newThread() {
@Override
public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // a. Define the Message to be sent Message MSG = message.obtain (); msg.what = 1; // Message identifier msg.obj ="AA"; Send showhandler.sendMessage(MSG) to MessageQueue; } }.start(); // 3. Start child thread 2 newThread() {
@Override
public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // a. Define the Message to be sent Message MSG = message.obtain (); msg.what = 2; // Message identifier msg.obj ="BB"; Send showhandler.sendMessage(MSG) to MessageQueue; } }.start(); } // Analysis 1: custom Handler subclass // set to: Private static class FHandler extends Handler{// Define WeakReference<Activity> reference; Public FHandler(Activity Activity) {// use WeakReference WeakReference to hold Activity instance reference = new WeakReference<Activity>(activity); } @override public void handleMessage(Message MSG) {switch (MSG. What) {case 1:
Log.d(TAG, "Message received from thread 1");
break;
case 2:
Log.d(TAG, "Message received from thread 2");
break; }}}}Copy the code
Solution 2: Empty the message queue inside the Handler when the external class ends its life cycle
-
The principle not only eliminates the “unprocessed/processed message -> Handler instance -> external class” reference relationship, but also synchronizes the Handler lifecycle (i.e. the period in which the message exists) with the lifecycle of the external class
-
The specific plan When an external class (here in the Activity, for example) the end of the life cycle (the system will call onDestroy ()), to remove all the messages from the message queue Handler (call removeCallbacksAndMessages (null))
-
Specific code
@Override
protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); // Emptying the message queue when the external class Activity life cycle ends & ending the Handler life cycle}Copy the code
Use advice
To ensure that all messages in the message queue in Handler can be executed, solution 1 is recommended to solve the memory leak problem, which is static inner class + weak reference
4. To summarize
- This article mainly explains
Handler
Knowledge of memory leaks: Principles & Solutions - And I’m going to continue with that
Android
Development of knowledge about memory leaks, interested can continue to pay attention toCarson_Ho android Development Notes