This is the third day of my participation in the August More text Challenge. For details, see: August More Text Challenge
Familiar yet unfamiliar handler-1
Handler three-piece creation process:
The construction method is as follows:
public Handler(Callback callback, boolean async) {
// If the flag is true
/ / will test Handler at run time whether the implementation of static (anonymous | | inner classes | | local class)
// Will print wraning hints at runtime that may cause a memory leak
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) = =0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: "+ klass.getCanonicalName()); }}// If the Handler constructor does not retrieve the Looper object of the current thread when it is called
// Throw an exception because Handler in this state is completely unavailable
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// Let the current Handler also hold the MessageQueue of the current thread's Looper
mQueue = mLooper.mQueue;
mCallback = callback;
// If this Handler sends an asynchronous Message, it will send an asynchronous Message.
mAsynchronous = async;
}
Copy the code
For the main thread, Looper is created in activityThread. main by calling prepareMainLooper:
public static void prepareMainLooper(a) {
Looper.prepare is also used to create the function, but quitAllowed is set to false
// indicates that the Looper will never quit voluntarily
prepare(false);
synchronized (Looper.class) {
if(sMainLooper ! =null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// Assign the main thread's Looper object reference to the static variable sMainLoopersMainLooper = myLooper(); }}Copy the code
Looper is created by calling the prepare method (or static looper. prepare if we need to create our own handler-looper) :
// Looper the Looper created by the exposed prepare method will exit voluntarily
public static void prepare(a) {
prepare(true);
}
// This static method is provided internally only,
// quitAllowed=false Only when Looper is created for the main thread
private static void prepare(boolean quitAllowed) {
// sThreadLocal is a thread-only container that holds Looper objects
// If the thread has already created a Looper, calling prepare again will cause an error
if(sThreadLocal.get() ! =null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// Create a Looper object and store it in sThreadLocal
sThreadLocal.set(new Looper(quitAllowed));
}
Copy the code
In the constructor of Looper, however, only two things are done:
private Looper(boolean quitAllowed) {
// Create the MessageQueue object
mQueue = new MessageQueue(quitAllowed);
// Get the current thread instance
mThread = Thread.currentThread();
}
Copy the code
In the construction of MessageQueue, the most important thing is to call nativeInit to complete the initialization on the Native side, which will be analyzed later.
So let’s make a brief summary:
There are several objects involved: Handler, Looper, and MessageQueue. The role of these three objects is well known……
- Handler: Is responsible for sending and processing messages.
- MessageQueue: A queue containing Message objects, which is essentially a data structure.
- Looper: a three-piece driver that retrieves messages from the message queue and sends them to the corresponding Handler.
The Message object mentioned above, which we’ll look at later, is essentially just a data structure that holds some data.
Looper.loop The key to start Looper:
If we simply construct a Handler and Looper object to send a message, we will find that the entire system is not working because we do not call looper. loop to open the MessageQueue loop.
In ActivityThread’s main method, looper. loop is called after Looper is created for the main thread, so if we create a Handler in the main thread, we don’t need to call any other Api to send messages.
Looper.loop code is as follows:
public static void loop(a) {
// Still do some necessary verification work
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// Message queue for current Looper
final MessageQueue queue = me.mQueue;
// ...
// An infinite loop to consume Message objects in the queue
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// Exit the loop when there are no more messages in the queue
return
}
// Omit some trace related code
try {
// Distribute the message to the corresponding target
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
// Omit some code such as log
// Message is reusable. After a message is distributed, resources are recycledmsg.recycleUnchecked(); }}Copy the code
You can see the two core things looper. loop does:
- The driver loop keeps fetching messages from MessageQueue.
- Distribute Message to message.target.
Message Specifies the data to be consumed
What is Message. Target?
To send a message, we call either the HANder. postXXX or the Handler.sendXXX apis. PostXXX means to send a Runnable. The former postXXX series API is based on the latter sendXXX series. The Runnable sent by postXXX is ultimately encapsulated as Message (message.callback member).
Message creation, before actually calling the SEND API, mostly involves the following operations:
// In order to realize object reuse, there may be a large number of messages in the process
Message m = Message.obtain();
// Obtain the following:
public static Message obtain(a) {
// sPoolSync is an Object, used here as a Lock
synchronized (sPoolSync) {
// Store all Message caches in a linked list
// When needed, take the list one at a time.
if(sPool ! =null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
returnm; }}// If there is none in the queue, create it again, after a Message has been consumed by the looper. loop distribution
// The corresponding recycling will be carried out.
return new Message();
}
Copy the code
Finally, messages are sent in the sendMessageAtTime API:
// Message is the Message object to be sent, and uptimeMillis indicates the time that the Message needs to be processed
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
// Check work
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);
}
// Queue the message:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
// In this case, the target is the Handler that sends the message.
msg.target = this;
// If the Handler constructor sets this Handler to send asynchronous messages
// This will mark the Message object as an asynchronous Message, which will be analyzed later.
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Copy the code
As described above, the Handler packages itself and the Message or task (Runnable) into the Message, and then calls MessageQueue’s enqueuing method to queue the Message.
Message.target is actually the Handler itself. The Handler dispatch method is as follows:
public void dispatchMessage(Message msg) {
// If you send a runnable, then runnable.run is called to run the task to be executed
if(msg.callback ! =null) {
handleCallback(msg);
} else {
// If the callback field is passed when the Handler is created
// Then the consumption of the message is passed to the callback
if(mCallback ! =null) {
if (mCallback.handleMessage(msg)) {
return; }}// Otherwise, call the handleMessage waiting for the subclass to override the message
// Leave that to a concrete subclass of Handler.handleMessage(msg); }}Copy the code