Android Component Handler Looper Message Understanding

Android Component_Handler Looper Message Understanding

1. Overview of Handler mechanism

The Handler mechanism is a message processing mechanism in Android.
Major components or key concepts:
1. Message, the data unit for inter-thread communication.
2. Message Queue, Message Queue, which stores messages published by Handler and executes according to FIFO.
3. Handler is the primary handler of a Message and handles messages in a Message queue by adding messages to the Message queue comments.
4. Looper Loop, which loops away messages from Message Queue and delivers them to the appropriate Handler for processing.
5. Thread UI thread s are usually the main thread for which a Message Queue is created when Android starts the program.
Each thread can contain a Looper object and a MessageQueue data structure.
6. ThreadLocal his role is to help Handler get the ooper of the current thread (multiple threads may have multiple Looper s)

2. Use scenarios

  1. We often use Handler to update the UI, but not to say that Handler is the one that updates the UI, time-consuming I/O operations, reading files, accessing the network, and so on.

  2. Subthreads communicate, you can create a handler in one subthread, then use this handler instance to send messages in any other thread, and the code that eventually processes the messages will run in the thread where you created the handler instance.

  3. Delay tasks, etc.

3. Usage

3.1. Update UI in child thread

public class MainActivity extends AppCompatActivity {
    //handler in main thread
    Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //Get the Message object you just sent and do the UI here
            Log.e(TAG,"------------> msg.what = " + msg.what);
            ...
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //Send an update message to the handler of the main thread in the child thread to update the UI
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message m = mHandler.obtainMessage();
                ...
                mHandler.sendMessage(m);
            }
        }).start();
    }
}

3.2 Interthread Communication

        final Handler[] h = new Handler[1];
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();//To create a looper for a child thread, the child thread uses handler messaging, which must be done because the default child thread does not have a Looper object
                h[0] = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.e(TAG,"------------> msg.what = " + msg.what);
                    }
                };
                Looper.loop();//Message Pool Message Cycle Processing
            }
        }, "work1").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message msg = h[0].obtainMessage();
                //msg.sendToTarget();
                h[0].sendEmptyMessage(0);//Send messages to work1 using handler holding looper for work1 subthread in work2 subthread for message processing
            }
        }, "work2").start();

3.3 Use of HandlerThread

    {
        MyHandlerThread mHandlerThread = new MyHandlerThread("work");
        Handler mHandler = new Handler(mHandlerThread.getLooper()){//Associate the mHandler with the Looper of the HandlerThread object
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    }

    MyHandlerThread mHandlerThread = new MyHandlerThread("work");
        Handler mHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    }

3.4 Handler CallBack parameter use
The CallBack parameter can be passed in when constructing the Handler object as follows

//Main thread:
  Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });
//In other threads:
handler.sendMessageXXX(msg);

3.5 Handler post method uses

    //Create mPostHandler in main thread
    Handler mPostHandler = new Handler();
    //Use post method in child threads
     new Thread(new Runnable() {
            @Override
            public void run() {
               /* Message m = mHandler.obtainMessage();
                mHandler.sendMessage(m);*/
                mPostHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        //Update the UI, note that although the operation is written in a sub-thread, the fact runs in the main thread, Reason Analysis is in 4.2.5
                    }
                });
            }
        }).start();

3.6 Handler Delayed Tasks/Loop Timer Operations

Delay Task
new Handler().postDelayed(new Runnable(){   
            public void run() {  
                   //show dialog
            }  
  }, 5000); //Delay 5 seconds to execute runnable run method
Cycle Timer Operation:
1,Create one firstHandlerobject  
 Handler handler=new Handler();  

2,Then create one Runnable Yes
Runnable runnable=new Runnable(){  
   @Override  
   public void run() {  
    // TODO Auto-generated method stub  
    //What to do, call this Runnable object again here to implement timer operations every two seconds  
    handler.postDelayed(this, 2000);  
   }  
};  

3,Use PostDelayed Method, call this in two seconds Runnable object  
handler.postDelayed(runnable, 2000);  

4,If you want to turn off this timer, you can do so
handler.removeCallbacks(runnable); 

4. Principle Analysis

The iron triangle-Handler, Looper, and MesageQueue of the Handler mechanism are also analyzed shallowly below.
Overall architecture:

4.1 The main thread Handler object is associated with the main thread Looper object

A shallow analysis of the example in 3.1 is provided.

4.1.1 Handler Object Initialization Gets the ooper object for the main thread

1. Main thread Handler object initialization:

Handler mHandler = new Handler(){...}

The default constructor associates the Handler object with the ooper object of the current thread.Let's see how the constructor relates the looper of the current thread.
Handler's default constructor:

frameworks/base/core/java/android/os/Handler.java
    public Handler() {
        this(callback:null, async:false);
    }

2. If the thread does not have a looper object, the handler will throw an exception:

    public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();//
        if (mLooper == null) {//An exception will be thrown if the current thread does not have a Looper object
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

3.The sThreadLocal object holds the looper object for the current thread, Looper.myLooper() is the ooper object used to get the current thread:

frameworks/base/core/java/android/os/Looper.java
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();//Get the ooper object for the current thread here
    }

4.1.2 Create Looper and MessageQueue objects when the main thread initializes

1. For UI threads, which we call Main threads and ActivityThread, when ActivityThread was created, Looper objects were created in the main function and entered into the message loop through Looper.loop():

frameworks/base/core/java/android/app/ActivityThread.java
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper(); //Create Main Thread Looper Object

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        ...
        Looper.loop();//Cycle through messages in MessageQueue
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

2.Looper.prepareMainLooper() creates the ooper and MessageQueue for the main thread and opens the message loop for the main thread through Looper.loop():

frameworks/base/core/java/android/os/Looper.java
    public static void prepareMainLooper() {
        prepare(false);//Create a Looper object and associate the new looper object with the current thread
        synchronized (Looper.class) {
            if (sMainLooper != null) { //You can see that the main thread should have only one Looper object
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();//Returns the ooper object for the current thread
        }
    }

3. The Looper.prepare() function creates a Looper object for the current thread (here the main thread, or another thread) and sets the object to the thread's sThreadLocal variable:

  private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) { // Explains that prepare cannot be called twice, otherwise an exception will be thrown, guaranteeing that a thread has only one Looper instance
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));//sThreadLocal is a ThreadLocal object that stores variables in a thread.
    }

4.Looper.myLooper() returns the ooper object for the current thread, which verifies that myLooper() does get the ooper object for the current thread from the sThreadLocal variable.

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

To summarize, the main thread initially generates a Looper object with a non-exitable MesageQueue and stores the Looper object in the sThreadLocal variable of the main thread.
When a Handler object is created in the main thread, it is associated with the main thread Looper object stored in the sThreadLocal variable of the current thread.
Therefore, in the example of 3.1 above, when the main thread new a Handler object associates the Handler object with the Looper object in the main thread, Looper.loop() is executed to process messages in a continuous loop;

4.2 Brief Analysis of Handler, Looper, Message and Handler Thread

4.2.1 Handler

1. Methods available

post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageDelayed(Message,long)
sendMessageAtTime(Message,long) 

2. All of the above methods will eventually call sendMessageAtTime(), add the message to the message queue held by Looper associated with the target Handler object of msg, and then queue the message queue to cycle through the message:

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis); //Add the message to the message queue in the looper associated with the message's target handler
    }

4.2.2 Looper:

1.Looper constructor, you see a new MessageQueue object in Looper's constructor:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);//Create Message Queue
        mThread = Thread.currentThread();
    }

2.Looper.loop(), a function that loops through messages in a message queue:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    public static void loop() {
        ...
        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // May block, if there is no message it will block here.
            ...
            try {
                msg.target.dispatchMessage(msg); //Call the dispatchMessage method of the handler that sent the message;
            } finally {
            }
            ...
            msg.recycleUnchecked();//The processed msg can be recycled, and the implementation clears the contents of the msg object and keeps it in the message pool for recycling            
        }
    }

3. Processing messages in dispatchMessage, where the runnable in the post method is msg.callback

frameworks/base/core/java/android/os/Handler.java
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg); //Execute the runnable method in the post method
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) { //If a callback is passed in when a handler is created
                    return;
                }
            }
            handleMessage(msg);//Replicate the handleMessage() method to process messages when creating handler s
        }
    }

3.1 will first determine that the msg.callback memory does not exist, msg.callback is a Runnable type, if msg.callback exists, then the Message is put into the Message queue by executing the Handler's postXXX series method. In this case handleCallback (msg) will be executed, and the handleCallback source code is as follows:

private static void handleCallback(Message message) {
  message.callback.run();
}

So we clearly see that we executed the run method of msg.callback, which is the run method of the Runable object passed by postXXX.
3.2. If we don't put the Message into the Message queue through the postXXX family method, then msg.callback is null, and the code continues to execute, then we will judge that the member field mCallback memory of Handler does not exist.MCallback is of type Hanlder.Callback. As mentioned above, in the constructor of Handler we can pass an object of type Hanlder.Callback which needs to implement the handleMessage method. If we pass the Callback object in the constructor, we will have the handleMessage side of the CallbackMethod to process messages.
3.3. If we do not pass in a Callback type object in the constructor, then mCallback is null, then we will call Handler's own hanldeMessage method, which is empty by default and we need to override ourselves to implement it.
To summarize, we can see that Handler provides three ways to process messages, with priority given to the former: first, try to have the Runable passed in postXXX execute, second, try to have the handleMessage method of the Callback passed in the Handler constructor handle, and finally, let Handler handle its own handleMessage methodReason Message.

4.2.3 MessageQueue

next() method of 1 MessageQueue
The next method is an infinite loop in which messages are constantly fetched from the message queue, and if a message is returned and removed from the chain table, no message is always blocked.

Message next() {
        for (;;) {
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    ...
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } 
        }
    }

4.2.4 HandlerThread

1. Thread with Looper:

frameworks/base/core/java/android/os/HandlerThread.java
public class HandlerThread extends Thread {
@Override
    public void run() {
        ...
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        onLooperPrepared();
        Looper.loop();
        ...
    }
}

We see that HandlerThread inherits from Thread and uses Looper.prepare() in the run function to create a Looper object for use with a thread and places the object in a variable within the thread scope (sThreadLocal); during the construction of the Looper object, a MessageQueue is initialized as the Looper objectMember variable; loop() is turned on, continuous loops cancel message processing from MessageQueue, block when there is no message, and wake up when there is one.
So you can also use HandlerThread, which eliminates the need for Looper.prepare() and Looper.loop() as normal threads do, because HandlerThread is ready for us;

2. Additionally, the Looper object of the HandlerThread can be obtained by the getLooper method of the HandlerThread:

    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

handleMessage

post method in 4.2.5 Handler

In the example in 3.5, the run method in post in the sub-thread runs in the main thread for the following reasons:
1. Incoming Runnable Object

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

2. Encapsulate Runnable into a Message object here

  private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r; //Pass runnable into callback variable in message object
        return m;
    }

3. Consistent with the normal handler sendMessage method flow, the call uses sendMessageAtTime to add the message to the target handler of the message and to the message queue in the looper associated with the mPostHandler, as mentioned in 4.2.1.

  public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

That is, although the runnable replication method is in the child thread, it is also in the Message Queue object held by looper encapsulating the message object into the main thread, where messages encapsulating the runnable variable are processed, so the run method in the post runs in the main thread.

5. Notes

  1. Handler handles messages with a strict distinction between whether it is in a UI thread or not. Handler is generally used to deliver messages in non-UI threads and to send messages in non-UI threads.
    Processing is strictly enforced, but if Handler is used to send messages in the UI thread, the same messages are merged internally and the timing of execution is not guaranteed
  2. Because HandlerThread has its own message queue, it does not interfere with or block UI threads and is better suited for tasks that take longer to process.We just need to send the task to HandlerThread and wait for the notification to return to the main thread when the task finishes executing.
  3. Handler usage in android should be aware of OOM memory leaks caused by handler) http://blog.csdn.net/javazejian/article/details/50839443
  4. When using mechanisms in normal threads, remember to Looper.prepare(); and finally Looper.loop();

6. Case Analysis

Make it up later

Keywords: Java Android network

Added by MachineHead933 on Sat, 06 Jul 2019 21:08:19 +0300