Directory: 1. Definition of memory leak 2. Optimization scheme

1. Memory leak definition

First we need to learn about common memory allocation in Java, including static storage areas (method areas), stacks, heaps, and so on.

Static storage: Stores static methods and global static data and constants, etc. The memory of this area is allocated when the program is compiled and exists throughout the entire process of running the program. Stack area: During the execution of a method, local variables in the method body (including the underlying data types and object references, etc.) are created on the stack. After the execution of a method, the memory held by local variables in this area is released automatically. Stack memory allocation operations are built into the processor’s instruction set, which is efficient, but the capacity of this region is limited. Heap area: also known as dynamically allocated area, usually stores instances of new objects, which are reclaimed by the GC if they are not referenced.

As a result, we often say the memory leak is to point to: constantly create objects on the heap area, at the end of the object has been used, no longer use the object, but there are other objects (a longer life cycle) reference, lead to the object can be collected by GC recycling, lead to heap area can use less memory, causing the memory leak, the final result is OOM. In fact, the cause of memory leaks in Android is similar to that in Java: objects with long lifetimes hold references to objects with short lifetimes.

2.Handler Causes memory leakage

In Android, cross-thread interaction, especially the interaction between the child thread and the UI thread, is transmitted through the Handler in the child thread, and the Handler does the OPERATION of updating the UI, as shown below:

private Hand = new Handler(){
       @Override
       public void handleMessage(Message msg) {
           super.handleMessage(msg);
           switch (msg.what){
               caseUPDATE_UI: // Update UI operationsbreak; }}};Copy the code

So why is it possible to send messages in child threads through handlers, and update the UI in handleMessage? We know that UI updates are usually performed only on the main thread (usually the UI thread), so we end up calling UI thread updates. The analysis of the call process is as follows:

2.1 Creating a Handler Object

When we create a Handler object, it is bound to the thread and its message queue. If it is not bound to the current thread and the thread queue, it will not be able to handle the event distribution properly. We create a Handler on the main thread, and therefore bind it to the main thread. The Handler object implicitly holds a reference to an external object, usually an Activity.

When you create a new Handler, it is bound to the thread / * message queue of the thread that is creating it

2.2 Message queue processing

When the child thread finishes processing, it sends the message to Handler, but if the current interface has been destroyed, normally, if the Activity is not in use, it may be collected by GC, but because the child thread has not finished processing. The Handler still holds a reference to the Handler (otherwise it cannot send Handler messages properly), and the Handler holds a reference to the external Activty, causing the Activity to fail to recycle properly, resulting in a memory leak. The chain of references is as follows: MessageQueue -> Message -> Handler -> Activity

3. Optimize the plan

  1. Clean up the message queue when the Activity is destroyed;
  2. Custom static Handler class + soft reference.

3.1 Clear the message queue when the Activity is destroyed

The Activity is destroyed, call removeCallbacksAndMessages clear Message and Runnable.

 mHandler.removeCallbacksAndMessages(null);
Copy the code
/* * Remove any pending posts of callbacks and sent messages whose
     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
     * all callbacks and messages will be removed.
     */
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }
Copy the code

3.2 Custom static Handler class + weak reference

static class MyHandler extends Handler {
        WeakReference<Activity> mWeakReference;
        private MyHandler(WeakReference<Activity> mWeakReference){
            this.mWeakReference = mWeakReference;
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(mWeakReference! =null){ Activity activity = mWeakReference.get();if(activity! =null){// Handler message processing}}}}Copy the code

It can be seen from the analysis of 3.1 that the main reason for memory leakage caused by Handler is that it holds the strong reference of the current Activty, which makes the Activity unable to be reclaimed in time. As we know, the mechanism for GC to deal with weak references is that when the object is destroyed, it will be reclaimed even if there are weak references.

4. To summarize

To avoid memory leaks with handlers, do the following:

  1. Clean up the handler’s message queue in the Activity’s onDestory() method.
  2. Custom static Handelr classes to avoid memory leaks caused by non-static inner classes;
  3. Weak references are used so that reference objects can be recycled in a timely manner.

Combining the preceding three methods, you can effectively avoid memory leakage caused by improper use of handlers.