Android Development Art Exploration-tenth chapter Android message mechanism reading notes
Handler is not designed to update the UI, but is often used to update the UI
Overview
Android's message mechanism is the main merit is handler operation mechanism, handler operation needs the bottom MessageQueue and looper support.
- MessageQueue is a message queue, as the name implies, it stores a set of messages internally, in the form of queues to provide insert and delete work. Although it is called a queue, the internal storage structure is not a real queue, but rather a single-linked list of data structures to store message lists.
- Looper meaning is a loop, which can be understood as a message loop. MessageQueue is just a storage unit of messages and cannot process messages, and Looper fills this function. Looper will look for new messages in the form of an infinite loop. If you have a message, do it or wait.
Looper also has a special concept, that is threadlocal. Threadlocal is not a thread, it is a function of storing data in each thread. Handler is created using the current thread's looper to construct the message circulatory system, handler internally uses threadlocal to get the looper of the current thread.
Theadloacl can provide data in different threads with no interference. The thread defaults to no threadloacl. If you need to use handler, you must create a looper for the thread. The main thread, the UI thread, is actcivitythread. The looper is initialized when Activitythread is created, so handler can be used by default in the main thread.
10.1 Overview of the Android messaging mechanism
Handler,looper,messagequeue is actually one of the three
The main function of handler is to switch a task to a specified thread to execute.
Why does Android offer this feature? Because Android specifies that the UI can only be done in the main thread, accessing the UI in a child thread throws an exception. The Viewrootimpl validates the UI operation, which is done by the Viewrootimpl Checkthread method.
void checkThread(){ if(mThread != Thread.currentThread()){ thrownew CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views" ); }}
Why is the system not allowed to access the UI in a child thread?
Android UI controls are not thread-safe, and concurrent access in multiple threads can cause UI controls to be in unexpected state.
Why does the system not add a lock mechanism to the UI control's access?
There are two disadvantages:
- Adding a lock mechanism complicates the logic of UI access;
- The lock mechanism reduces the efficiency of UI access because the locking mechanism blocks the execution of certain threads.
Overview of how handler works
Handler is created with the current thread's looper to build the internal message circulatory system, if the current thread does not have looper, it will be reported "Java.lang.RuntimeException:Cant's create handler inside Thread that have not called looper.prepare () ". This time you need to create a looper for the current thread.
Once handler is created, the internal looper and MessageQueue can work together with handler. A runnable is posted to the handler internal Looper via the handler post method, and a message can be sent to the handler via the Looper send method.
The Post method of handler is finally done by means of the Send method.
When the handler send method is called, handler calls MessageQueue's Enqueuemessage method to place the message in the message queue, and then looper the message when new information arrives, The Handlemessage method of the runnable or handler handler in the final message is called.
Note that Looper is running in the same thread as the creation of the handler, so that the business in the handler is switched to the thread where the handler was created to execute.
10.2Android message mechanism analysis of how 10.2.1 threadlocal works
Threadlocal is a data storage class inside a thread that can store data in a specified thread, and after the data is stored, the data can be obtained only in the specified thread and not on other threads.
Generally used to threadlocal less places.
Threadlocal Usage Scenarios:
- Threadloacl can be considered when some data is scoped to a thread and different threads have different copies of the data.
- Another usage scenario for Threadloacl is object delivery under complex logic.
The
case
First defines a ThreadLocal object, where the Booelan type is selected, as follows:
private threadlocal<boolean> mbooleanthreadlocal = new threadlocal<boolean> ();
The
then sets and accesses its value in the main thread, child thread 1, and child thread 2 respectively, as shown in the code below.
mbooleanthreadlocal.set ( True ); LOG.D (Tag, "[thread#mian]mbooleanthreadlocal=" +mbooleanthreadlocal.get ()); Span class= "Hljs-keyword" >new Thread ( "thread#1" ) { @Override puboic void Run () {Mbooleanthreadloacl.set (false ); LOG.D (Tag, "[thread#mian]mbooleanthreadlocal=" +mbooleanthreadlocal.get ()); }}.start (); new Thread ( "thread#2" ) { @Override Puboic void Run () {LOG.D (Tag,
In the preceding code, set the value of Mbooleanthreadlocal to true in the main thread, set the value of Mbooleanthreadlocal to false in child thread 1, and do not set the value of mbooleanthreadlocal in child thread 2. Then, respectively, in 3 threads through the Get method to get the value of mbooleanthreadlocal according to the description of the front face threadlocal, this time, the main thread should be true, child thread 1 should be false, and child thread 2 because there is no value set, So it should be null.
The results of the operation are as follows:
D/TestActivity(8676):[Thread#main]mBooleanThreadLocal=trueD/TestActivity(8676):[Thread#1]mBooleanThreadLocal=falseD/TestActivity(8676):[Thread#2]mBooleanThreadLocal=null
The reason why Threadlocal has such a wonderful usage is that it accesses the same threadlocal get method in different threads, and threadlocal internally extracts an array from the respective thread. The corresponding value value is then looked up from the array based on the index of the current threadlocal.
Threadlocal is a generic class that is defined as having public class ThreadLocal<T>
a set and get method inside.
Threadloacl the Set Method:
publicvoidset(T value){ ThreadLocal currentThread = Thread.currentThread(); Values values = values(c); ifnull){ values = initializeValues(currentThread); } values.put(this,value);}
In the set method, the values method is first used to get the THREADLOACL data in the current thread. Within the thread class, there is a member dedicated to storing the thread's THREADLOACL data: Threadloacl.values. If localvalues is null, then it needs to be
The value of the THREADLOACL is then stored after initialization. There is an array inside Localvalues: The public Object[] table
value of THREADLOACL exists in the table array.
How the 10.2.2 Message queue works
Message Queuing on Android is the messagequeue,messagequeue that contains two operations: Insert and read. The read operation itself is accompanied by a delete operation, inserting and reading the corresponding method Enqueuemessage and next, where Enqueuemessage's role is to insert a message into the message queue, The role of next is to remove a message from the message queue and remove it from the message queue. Although MessageQueue is called Message Queuing, its internal implementation is not a queue, in fact it is a single-linked list of data structures to maintain the message lists, single-linked list on the insertion and deletion of comparative advantage.
The source code in the book did not understand, read a few more times
How the 10.2.3 Looper works
Looper plays the role of the message loop in the Android messaging mechanism. It will keep checking for new messages from the MessageQueue, and if new messages are processed immediately, they will be stuck there.
Constructs a method in which a MessageQueue is created, which is the message queue, and then the current object is saved.
privateLooper(boolean quitAllowed){ mQueue new MessageQueue(quitAllowed); mThread = Thread.currentThread();}
Handler work needs looper, no looper thread will error. Looper.prepare () creates a Looper for the current thread and then opens the message loop through Looper.loop ().
new Thread("Thread#2"){ @Override publicvoidrun(){ Looper.prepare(); new Handler(); Looper.loop(); }}.start();
Looper In addition to the prepare method, but also provides the Preparemainlooper method, this method is mainly for the main thread activitythread create looper use, and its nature will be achieved through the Preare method. Because of the special looper of the main thread, Looper provides a Getmainlooper method through which the looper of the main thread can be obtained from anywhere.
Looper can also exit, Looper provides quit and quitsafely to quit a looper.
Difference:
Quit will exit Looper directly. Quitsafely simply sets an exit flag and then exits the message queue before it is processed safely.
After Looper exits, messages sent through handler will fail, and this time the handler send method will return false. In a child thread, if you create a looper for it manually, you should call the Quit method to terminate the message loop after all things are done, or the child thread will remain in a waiting state, and if you exit Looper, the thread will terminate immediately. So it is advisable to terminate looper when it is not necessary.
One of the most important methods of Looper is the loop method, which will only really work if the loop is called.
Public void Static void Loop(){FinalLooper me = Mylooper ();if(Me = =NULL){Throw NewRuntimeException ("No Looper; Looper.prepare () wasn ' t called on the This thread. "); }FinalMessageQueue queue = Me.mqueue (); Binder.clearcallingidentity (); FianlLongIdent = Binder.clearcallingidentity (); for(;;) {Message msg = Queue.next ();if(msg = =NULL){//no message indicates the message queue is quitting. return; }//this must is in a local variable,in case a UI event sets the loggerPrinter longging = me.mlogging;if(Logging! =NULL) {Logging.println (">>>>>dispatching to"+msg.tartget +" "+msg.callback +":"+msg.what); } msg.target.dispatchMessage (msg);if(Logging! =NULL) {Logging.println ("<<<<<<finished to"+ Msg.target +" "+msg.callbak); }//make sure that during the course of dispatching the identity of the thread wasn ' t corrupted Final LongNewident = Binder.clearcallingidentity ();if(Ident! = newident) {LOG.WTF (TAG,"Thread identity changed from 0x"+ long.tohexstring (ident) +"to 0x"+ long.tohexstring (newident) +"while dispatching to"+ Msg.target.getClass (). GetName () +" "+ Msg.callback +"what ="+ msg.what); } msg.recyckeunchecked (); }}
The working process of the Looper loop method:
The Loop method is a dead loop, and the only way to jump out of a loop is for the next method of MessageQueue to return null. When Looper's Quit method is called, Looper calls the Messagqueue's quit or Quitsafely method to notify the message queue to exit, and when the message queue is marked as exited, its next returns null. In other words, the looper must exit, or the loop method will loop indefinitely. The loop method calls the next method of MessageQueue to get the new message, and next is a blocking operation, and when there is no message, the next method blocks there. If MessageQueue's next method returns a new message, Looper will process the message: msg.target.dispatchingMessage(msg)
The msg.target here is the handler object that sends the message, so that the handler sends the message and finally gives it the DispatchMessage method to handle it. But the difference here is that the DispatchMessage method of handler is executed in the looper that was used to create the handler, which successfully switches the code logic to the specified thread to execute.
How the 10.2.4 handler works
Handler work mainly includes: the sending and receiving process of the message.
The message can be sent through a series of methods of Post and send a series of methods to achieve, post a series of methods are finally through the send a series of methods to achieve.
The process of sending a typical message:
Public Final Boolean SendMessage(Message msg) {returnSendmessagedelayed (MSG,0);} Public Final Boolean sendmessagedelayed(Message msg,LongDelaymills) {if(Delaymillis <0) {Delaymills =0; }returnSendmessageattime (Msg,systemclock.uptimemills () + delaymills);} Public Final Sendmessageattime(Message msg,LongUptimemills) {MessageQueue queue = Mqueue;if(Queue = =NULL) {RuntimeException E =NewRuntimeException ( This+"Sendmessageattime () called with no Mqueue"); LOG.W ("Looper", E.getmessage (), E);return false; }returnEnqueuemessage (queue,msg,uptimemills);}Private Boolean Enqueuemessage(MessageQueue queue, Message msg,LongUptimemills) {Msg.target = This;if(Masynchronus) {msg.setasynchronous (true); }returnQueue.enqueuemessage (msg,uptimemills);}
Handler the process of sending a message is simply inserting a message into the message queue, and the next method of MessageQueue returns the message to Looper,looper when the message is received, and the final message is processed by Looper to handler. That is, the handler Diapatchmessage method is called, at which point the handler enters the stage of processing the message.
Handler method of Diapatchmessage:
publicvoiddispatchMessage(Message msg){ ifnull){ handlerCallback(msg); }else{ ifnull){ if(mCallback.handleMessage(msg)){ return; } } handleMessage(msg); }}
The process of handler processing messages is as follows:
First, check whether the callback of the message is null and NOT NULL to process the messages through Handlecallback. The callback of the message is a Runnable object, which is actually the runnable parameter passed by the handler post method. The logic of Handlecallback is also very simple, as follows:
privatestaticvoidhandleCallback(Message msg){ message.callback().run();}
Second, check whether Mcallback is null and NOT NULL to call Mcallback's Handlemessage method to process the message. Callback is an interface.
publicinterface Callback{ publicbooleanhandleMessage(Message msg);}
Through callback, you can use: Handler handler = new Handler(callback);
Callback: You can use it to create an instance of handler, but you do not need to derive a subclass of handler.
In everyday development, the most common way to create handler is to derive a subclass of handler and override its Handlermessage method to handle specific messages, and callback gives us another way to use handler, when we don't want to derive subclasses, Can be achieved by callback.
Finally, call handler's Handlemessage method to process the message.
The flowchart is as follows:
Handler also has a special construction method, which is to construct the handler through a specific looper, which is implemented as follows:
publicHandler(Looper looper){ this(looper,null,false);}
The following is a default construction method for Handler pubic Handler (). As you can see from the code in the method, if the current thread does not have Looper, it will throw "can ' t create handler inside thread that have not called looper.prepare ()"
publicHandlerboolean asyn){ ··· mLooper = Looper.myLooper(); ifnull){ thrownew RuntimeException( "Can‘t create handler inside thread that has not called Looper.prepare()" ); } mQueue = mLooper.mQueue(); mCallback = callback; mAsynchronous = async;}
10.3 The message loop of the main thread
The main thread of Android is Activitythread, the main thread of the entry method is main, in the main method, the system will be Looper.loop () to open the main thread of Looper and MessageQueue, Turn on the main thread's message loop via Looper.loop.
Public Static void Main(String[]args) { ... Process.setargv0 ("<pre-initialized>"); Looper.preparemainlooper (); Activitythread thread =NewActivitythread (); Thread.attach (false);if(Smainthreadhandler = =NULL) {Smainthreaddhandler = Thread.gethandler (); } asynctask.init ();if(false) {Looper.mylooper (). Setmessagelogging (NewLogprinter (Log.debug,"Activitythread")); ); } looper.loop ();Throw NewRuntimeException ("Main thread loop unexpectedly exited");}
After the start of the message loop for the main thread, Activitythread also needs a handler to interact with the message queue, which is ActivityThread.H, which defines a set of message types, including the start and stop processes of the four main components.
Activitythread inter-process communication through Applicationthread and AMS, AMS completes Activitythread request callback Applicationthhread Binder method in the way of interprocess communication, Then Applicationthread sends a message to H, and H receives a message that switches logic from Applicationthread to Acticvitythread.
Android Development Art Exploration-tenth chapter Android messaging mechanism