The handler mechanism includes the following classes:

  • Handler: Sends and receives messages
  • “Message” : Message carrier
  • MessageQueue: queue for storing messages
  • Looper: Enables polling for messages
Why update UI on main thread?

A: System designer, in order to consider the user experience, system performance

What if the child thread wants to update the UI?

A: Send a message to the main thread to update the UI through a messaging mechanism called Handler

Is the Handler only used to update the UI?

A: Handler is used for thread communication. If a child thread communicates with a child thread, the Looper initialization may occur in the child thread. How to ensure that the Looper object is correctly fetched in the child thread for loop polling? The answer is a ThreadLocal

What is a ThreadLocal?

A ThreadLocal is not a Thread, but a local variable of a Thread. ThreadLocal was born to solve the concurrency problem of multiple threads. It allows variables to be copied independently in each thread, without being read by one thread and modified by another thread.

// The hander receives the message looper.prepare () on the thread on which the two methods are executed. Looper.loop();Copy the code
Key points:

So Handler is not just for child threads to update the UI, but more importantly for communication between threads, and that’s why you can use Handler child threads to send messages to the main thread to update the UI

Analog Hander: Simple handwritten Hander message mechanism

Public class Message {//target stores handerd object Handler target; public int what; public Object obj; public Message(){ } @Override public String toString() { return obj.toString(); }}Copy the code
Public class MessageQueue {// mutex and condition variables Lock Lock; Condition notEmpty; Condition notFull; Message[] items; int putIndex; int takeIndex; int count; public MessageQueue(){ this.items = new Message[50]; this.lock = new ReentrantLock(); this.notEmpty = lock.newCondition(); this.notFull = lock.newCondition(); Public void enqueueMessage(message message){try {lock.lock(); // Queue full, block while (count == items.length){try {notfull.await (); } catch (InterruptedException e) { e.printStackTrace(); Items [putIndex] = message; PutIndex = (++putIndex == items.length)? 0 : putIndex; count++; Notempty.signalall (); notempty.signalAll (); }finally { lock.unlock(); Public Message next(){Message MSG = null; try{ lock.lock(); while(count == 0){ try { notEmpty.await(); } catch (InterruptedException e) { e.printStackTrace(); } } msg = items[takeIndex]; items[takeIndex] = null; // Loop the value takeIndex = (++takeIndex == items.length)? 0 : takeIndex; count--; // There is a new empty position, which tells the producer to produce notFull.signalAll(); }finally { lock.unlock(); } return msg; }}Copy the code
public class Looper { static ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); MessageQueue mQueue; private Looper(){ mQueue = new MessageQueue(); } /** * Public static void prepare(){if (sthreadLocal.get ()! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } /** * public static void loop(){Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } MessageQueue queue = me.mQueue; for(;;) { Message msg = queue.next(); if(msg == null){ continue; } / / forward MSG. Target. DispatchMessage (MSG); Public static Looper myLooper(){return sthreadLocal.get (); }Copy the code
public class Handler { private Looper mLooper; Private MessageQueue mQueue; public Handler() { mLooper = Looper.myLooper(); mQueue = mLooper.mQueue; Public final void sendMessage(Message MSG){MSG. Target = this; Mqueue.enqueuemessage (MSG); } public void dispatchMessage(MSG){handleMessage(MSG); Public void handleMessage(Message MSG){}Copy the code

Let’s write a Test class to Test the Hander mechanism that we just wrote

Public class Test {public static void main(String[] args) {// Thread 0:Thread 0 new Thread(new Runnable() {@override public  void run() { Looper.prepare(); Final Handler Handler = new Handler(){@override public void handleMessage(Message MSG){// Received the Message System.out.println(Thread.currentThread().getName() + ",received:"+msg.toString()); }}; New Thread(new Runnable() {@override public void run() {for (int I = 0; i < 5; i++) { Message msg = new Message(); msg.what = i; msg.obj = Thread.currentThread().getName() + ",send message:"+ UUID.randomUUID().toString(); System.out.println("send:"+msg); handler.sendMessage(msg); } } }).start() Looper.loop(); } }).start();Copy the code

Print statement: You can see that all messages sent by thread 1 are received by thread 0

send:Thread-1,send message:ecb68c8c-97e9-43ef-8be6-b71f58b731d1
send:Thread-1,send message:1d18184f-4fef-4fdb-8485-b6e336e841c6
Thread-0,received:Thread-1,send message:ecb68c8c-97e9-43ef-8be6-b71f58b731d1
send:Thread-1,send message:9382768d-46f6-4d2b-95d8-c2bdff05b85d
Thread-0,received:Thread-1,send message:1d18184f-4fef-4fdb-8485-b6e336e841c6
Thread-0,received:Thread-1,send message:9382768d-46f6-4d2b-95d8-c2bdff05b85d
send:Thread-1,send message:78451cff-0e4f-4781-bdbd-6ad300f62ab9
send:Thread-1,send message:34536c77-8324-4c59-a179-00ce589de467
Thread-0,received:Thread-1,send message:78451cff-0e4f-4781-bdbd-6ad300f62ab9
Thread-0,received:Thread-1,send message:34536c77-8324-4c59-a179-00ce589de467
Copy the code

What if multiple child threads send messages?

Public class Test {public static void main(String[] args) {// Thread 0:Thread 0 new Thread(new Runnable() {@override public  void run() { Looper.prepare(); Final Handler Handler = new Handler(){@override public void handleMessage(Message MSG){ System.out.println(thread.currentThread ().getName() + ",received:"+ msg.tostring ()); }}; for (int i = 0; i < 3; New Thread(new Runnable() {@override public void run() {for (int I = 0; i < 3; i++) { Message msg = new Message(); msg.what = i; msg.obj = Thread.currentThread().getName() + ",send message:"+ UUID.randomUUID().toString(); System.out.println("send:"+msg); handler.sendMessage(msg); } } }).start(); } Looper.loop(); } }).start(); }}Copy the code

Print statement: You can see that the message sent by each thread is received in thread 0

send:Thread-3,send message:40ffbe1c-4234-4d4d-9ace-2433deab14d4
send:Thread-2,send message:f0f38098-86be-431d-962b-6e1029419ff2
send:Thread-1,send message:fe0f68a1-e91c-4840-a6d4-803ab3070e0a
Thread-0,received:Thread-3,send message:40ffbe1c-4234-4d4d-9ace-2433deab14d4
send:Thread-2,send message:a379f356-e9b2-4ccd-84a9-dabc211b826e
send:Thread-3,send message:3dbab897-00b6-4448-a78b-36455dca35aa
Thread-0,received:Thread-2,send message:f0f38098-86be-431d-962b-6e1029419ff2
Thread-0,received:Thread-1,send message:fe0f68a1-e91c-4840-a6d4-803ab3070e0a
send:Thread-3,send message:90446fe2-ecd7-4734-bf79-7a5ae9430c5c
Thread-0,received:Thread-2,send message:a379f356-e9b2-4ccd-84a9-dabc211b826e
Thread-0,received:Thread-3,send message:3dbab897-00b6-4448-a78b-36455dca35aa
Thread-0,received:Thread-3,send message:90446fe2-ecd7-4734-bf79-7a5ae9430c5c
send:Thread-1,send message:667286f0-d73e-4960-97de-37ae01ff3312
send:Thread-2,send message:49643caa-7bf7-4d60-88ca-115118dc9648
Thread-0,received:Thread-1,send message:667286f0-d73e-4960-97de-37ae01ff3312
Thread-0,received:Thread-2,send message:49643caa-7bf7-4d60-88ca-115118dc9648
send:Thread-1,send message:65782fb4-cc21-4366-a976-6c72d6128c2f
Thread-0,received:Thread-1,send message:65782fb4-cc21-4366-a976-6c72d6128c2f
Copy the code