Thread message communication and asynchronous processing, and thread message Asynchronization
Reprinted please indicate the source:
Http://blog.csdn.net/yujun411522/article/details/46444869
This article is from: [yujun411522 blog]
The knowledge about message communication and handler in android has been briefly introduced in the previous Handler. Here we will introduce the implementation mechanism in the native layer. I believe everyone knows how to write a standard logoff thread:
Public MyLooperThread extends Thread {private Handler mHandler; public void run () {// you must call logoff before instantiating handler. prepare () method // 1 logoff. prepare () method logoff. prepare (); // 2 instantiate handler // mHandler = new Handler () {public void handleMessage (Message msg) {// process msg operations}; // 3 logoff. loop () keeps retrieving information from the message queue. loop ();}}
Typical three operations: 1. Call the logoff. prepare method to enter the preparation stage. 2. instantiate the Handler object. 3. Call the logoff. loop Method to enter the loop. Note: The order of the three cannot be wrong. The following describes
7.1 logoff. prepare enters the preparation stageFirst, let's look at the prepare method in the logoff class:
Public class logoff {// sThreadLocal. get () will return null unless you 've called prepare (). static final ThreadLocal <Looper> sThreadLocal = new ThreadLocal <lolocal> (); final MessageQueue mQueue; // internally maintains a Message Queue final Thread mThread; // the corresponding Thread volatile boolean mRun; // running status private static logoff mmainlogoff = null; // guarded by logoff. class, main thread logoff public static void prepare () {if (sThreadLocal. get ()! = Null) {// The logoff object has been set before. An exception is reported, indicating that Only one throw new RuntimeException ("Only one logoff may be created per thread") can be set once;} sThreadLocal. set (new Looper (); // set Looper for the sThreadLocal variable. This Looper must have one and only one Looper, which cannot be set repeatedly }}
SThreadLocal is a member variable used to store the status of the current thread. The storage range is within the thread. When calling the prepare method, an exception is reported if the thread sets the logoff object. If the logoff object is not set, a new logoff () object is set for sThreadLocal. Then, let's look at the logoff constructor:
Private Looper () {// private method, which cannot be accessed externally. mQueue = new MessageQueue (); // construct a MessageQueue object. loagemaintains a message queue, this is important when mRun = true; // sets mRun to true mThread = Thread. currentThread (); // mThread is set to the current thread}
The most important thing is the creation of MessageQueue message queue. See its construction method:
MessageQueue () {nativeInit (); // This is a local method}
NativeInit is a local method. The implementation method is described in the android_ OS _MessageQueue.cpp file first. NativeMessageQueue class:
Class NativeMessageQueue {public: NativeMessageQueue ();~ NativeMessageQueue (); inline sp <Looper> getLooper () {return mloue;} // return the Looper variable. This Looper class is the Looper class in native, void pollOnce (int timeoutMillis) instead of the java layer; // It will be analyzed later and asked void wake (); // It will be analyzed later and written to the pipeline "W" private: sp <logoff> mlogoff; // maintain a logoff variable on the Native layer };
Let's take a look at the implementation of the nativeInit method in MessageQueue. android_ OS _MessageQueue_nativeInit function:
Static void android_ OS _MessageQueue_nativeInit (JNIEnv * env, jobject obj) {// obj is the java-layer MessageQueue object. // 1 create an NativeMessageQueue object, this is NativeMessageQueue * nativeMessageQueue = new NativeMessageQueue (); if (! NativeMessageQueue) {jniThrowRuntimeException (env, "Unable to allocate native queue"); return;} // 2 associate NativeMessageQueue with the MessageQueue object at the java layer, actually, the nativeMessagQueue object is saved to the mPtr variable in MessageQueue. android_ OS _MessageQueue_setNativeMessageQueue (env, obj, nativeMessageQueue );}
Two main tasks: 1. Create the NativeMessageQueue object; 2. Associate the MessageQueue object at the java layer with the NativeMessageQueue object.
7.1.1 create an NativeMessageQueue objectSee the NativeMessageQueue constructor:
NativeMessageQueue: NativeMessageQueue () {mLooper = Looper: getForThread (); // call getForThread to check whether the returned value is null if (mLooper = NULL) {// If the returned value is null, create a logoff and set mlogoff = new logoff (false); Logoff: setForThread (mlogoff );//}}
Check whether the Logoff: getForThread function is null. If it is null, construct a logoff object and call setForThread to set it to a unique logoff in the thread. the prepare () method is the same. Let's take a look at how the logoff object on the native layer is instantiated and its constructor:
Logoff: loaning (bool allowNonCallbacks): callback (allowNonCallbacks), mSendingMessage (false), mResponseIndex (0), mNextMessageUptime (LLONG_MAX) {int wakeFds [2]; // create an MTS queue. The read and write ends are wakeFds [0] And wakeFds [1] int result = pipe (wakeFds); mWakeReadPipeFd = wakeFds [0]. // read mWakeWritePipeFd = wakeFds [1]; // Write result = fcntl (latency, F_SETFL, O_NONBLOCK); // set the non-blocking mode result = fcntl (mWakeWritePipeFd, F_SETFL, o_NONBLOCK); // set the non-blocking mEpollFd = epoll_create (EPOLL_SIZE_HINT); // listen to the pipeline struct epoll_event eventItem; memset (& eventItem, 0, sizeof (epoll_event )); // zero out unused members of data field union eventItem. events = EPOLLIN; eventItem. data. fd = mWakeReadPipeFd; result = epoll_ctl (mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem); // only listens to mWakeReadPipeFd, that is, read end}
7.1.2 associate the java-layer MessageQueue object with NativeMessageQueueAssociation is completed by the android_ OS _MessageQueue_setNativeMessageQueue function:
static void android_os_MessageQueue_setNativeMessageQueue(JNIEnv* env, jobject messageQueueObj, NativeMessageQueue* nativeMessageQueue) { env->SetIntField(messageQueueObj, gMessageQueueClassInfo.mPtr, reinterpret_cast<jint>(nativeMessageQueue));}
Actually, the address of the nativeMessageQueue object is copied to the gMessageQueueClassInfo. mPtr variable in the MessageQueue of the java layer. What is mPtr in gMessageQueueClassInfo:
static struct { jfieldID mPtr; // native object attached to the DVM MessageQueue} gMessageQueueClassInfo;
It is instantiated in the register_android_ OS _MessageQueue function:
Int Queue (JNIEnv * env) {int res = Queue (env, "android/OS/MessageQueue", gMessageQueueMethods, NELEM (gMessageQueueMethods); jclass clazz; FIND_CLASS (clazz, "android/OS/MessageQueue"); // mPtre points to the mPtr member variable GET_FIELD_ID (gMessageQueueClassInfo. mPtr, clazz, "mPtr", "I"); return 0 ;}
The binding result is that the mPtr of MessageQueue in the java layer saves the address of the NativeMessageQueue variable, so that the NativeMessageQueue can be accessed through MessageQueue, so far the java layer MessageQueue has been constructed. We can see that the Java layer logoff. the prepare () function is used to hold a reference of MessageQueue and the current thread in the logoff, while the mPtr in MessageQueue points to the NativeMessageQueue object in the native layer, the NativeMessageQueue object saves the unique native logoff variable of the thread. The relationship between the four variables is as follows:
7.2 create and send messages for HandlerLogoff is used to drive messages, and where does logoff message come from,
Handler sending; Where Is The logoff message,
Handler Processing. Therefore, Handler is used to send and process messages.
7.2.1 Handler CreationSee the constructor without parameters:
Public Handler (){.... // return The logoff object of the current thread. myLooper (); if (mloexception = null) {throw new RuntimeException ("Can't create handler inside thread that has not called loled. prepare () ");} mQueue = mloue. mQueue; // assign the mQueue value in the logoff to the member variable mQueue mCallback = null of Handler ;}
In its construction, you must first use logoff. the mylogoff method gets the logoff object of the current thread. If it is null, an exception is reported. Therefore, before creating a Handler, you must call logoff first. prepare method.
7.2.2 create a messageSee the Handler mechanism for this part.
7.2.3 message sending1. the java layer is implemented through the sendMessageAtTime function either through post or send.
Public boolean sendMessageAtTime (Message msg, long uptimeMillis) // uptimeMillis absolute time. the start time is taken as the benchmark {boolean sent = false; MessageQueue queue = mQueue; // MessageQueue if (queue! = Null) {msg.tar get = this; // specifies that the msg handler is this sent = queue. enqueueMessage (msg, uptimeMillis); // call the enqueueMessage method of MessageQueue} else {.....} return sent ;}
Call the enqueueMessage method of MessageQueue
Final boolean enqueueMessage (Message msg, long when ){.... final boolean needWake; synchronized (this) {if (mQuiting) {return false; // The thread where the handler is located is exiting} else if (msg.tar get = null) {mQuiting = true ;} msg. when = when; // specify when the Message will be processed // the header of the mMessages Message queue. p indicates the current Message p = mMessages; // The message queue is empty, the new message needs to be processed immediately, or the new message processing time is earlier than the processing time of the first message of the queue if (p = null | when = 0 | when <p. when) {// if this is the case, you need to insert the new message to the first queue for processing msg. nex T = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up} else {// otherwise find a proper position to insert, message prev = null in chronological order; while (p! = Null & p. when <= when) {prev = p; p = p. next;} msg. next = prev. next; prev. next = msg; needWake = false; // still waiting on head, no need to wake up }}if (needWake) {// added to the message queue and is in the block state, need to wake up nativeWake (mPtr); // local method, mPtr is native address of NativeMessageQueue} return true ;}
The new message queue is in the block state and needs to be awakened. Call the local method nativeWake. mPtr is the native address of NativeMessageQueue. The corresponding native Implementation Method in the android_ OS _MessageQueue.cpp file is as follows:
Static void Merge (JNIEnv * env, jobject obj, jint ptr) {// ptr is the callback address NativeMessageQueue * NativeMessageQueue = reinterpret_cast <nativeMessageQueue *> (ptr ); return nativeMessageQueue-> wake ();}
Continue to call the wake method of nativeMessageQueue:
void NativeMessageQueue::wake() { mLooper->wake();}
Call the wake method of mlogoff. In The logoff. cpp file:
Void Looper: wake () {if (mPendingWakeCount ++ = 0) {mPendingWakeTime = systemTime (SYSTEM_TIME_MONOTONIC);} ssize_t nWrite; do {nWrite = write (mWakeWritePipeFd, "W", 1); // write a "W"} while (nWrite =-1 & errno = EINTR) to mWakeWritePipeFd; if (nWrite! = 1) {if (errno! = EAGAIN) {LOGW ("cocould not write wake signal, errno = % d", errno );}}}
Do you still remember the pipeline created when the logoff object is created in the native layer? MWakeWritePipeFd is the write end of the pipeline. Once the "W" character is written, the thread that processes the message will be awakened by I/O.
2 native layer can send messages, but it is not like sending messages through handler in the java layer, but sent through logoff objects in the native layer. Ultimately, it is implemented through the sendMessageAtTime method, in logoff. in the cpp file:
Void Logoff: sendMessageAtTime (nsecs_t uptime, const sp <MessageHandler> & handler, const Message & message) {size_t I = 0; {AutoMutex _ l (mLock ); // The size of the message queue in the native layer size_t messageCount = mMessageEnvelopes. size (); // locate the position it is inserted in chronological order while (I <messageCount & uptime> = mMessageEnvelopes. itemAt (I ). uptime) {I + = 1;} // use MessageEnvelope to encapsulate the sending time, messageHandler, and message in MessageEnvelope messageEnvelope (uptime, handler, message ); // insert it to the native Message Queue (mMessageEnvelopes. insertAt (messageEnvelope, I, 1); if (mSendingMessage) {return ;}} if (I = 0) {wake (); // The same effect as calling the nativeWake method on the java layer. Write a "W" to the pipeline writer "}}
In the native layer, the message sending process is to first locate the Insertion Location, then encapsulate the message, handler, sending time, and other information into MessageEnvelope, and finally insert it into the appropriate position of the message queue. It involves a struct: MessageEnvelope:
Struct MessageEnvelope {MessageEnvelope (): uptime (0) {} MessageEnvelope (nsecs_t uptime, const sp <MessageHandler> handler, const Message & message): uptime (uptime ), handler (handler), message (message) {} nsecs_t uptime; // execute the event sp <MessageHandler> handler; // execute the handler Message message; // message };
So far, the message has been successfully sent, and the next step is to retrieve and process the message.
7.3 logoff. loop process messages cyclicallyLet's take a look at the implementation of the logoff. logoff method:
Public static void loop () {Looper me = myLooper (); if (me = null) {// you can see that, the loop method throw new RuntimeException ("No lotime; low. prepare () wasn' t called on this thread. ");} MessageQueue queue = me. mQueue ;..... while (true) {// 1 retrieve the Message msg = queue from MessageQueue. next (); // might block if (msg! = Null) {if (msg.tar get = null) {return; // exit when no message exists }..... // send the message msg.tar get. dispatchMessage (msg );.... // reclaim the message msg. recycle ();}}}
There are two important operations. The most important first step is to retrieve the message from messageQuene; 2. Distribute the message processing function.
7.3.1 messageQuene. next get the appropriate message
Final Message next () {int nextPollTimeoutMillis = 0; // The next poll time. 0 indicates that the (;;){... // local method nativePollOnce (mPtr, nextPollTimeoutMillis); synchronized (this) {// synchronize final long now = SystemClock. uptimeMillis (); final Message msg = mMessages; // if (msg! = Null) {final long when = msg. when; // execution event of the first message of the team. Here we assume it is 4 s, now is 5 s if (now> = when) {// indicates that the first message of the team has arrived or an event has been executed, so mBlocked = false is immediately executed; mMessages = msg. next; // retrieves the first message from the queue and returns msg. next = null; msg. markInUse (); // indicates that return msg is being used;} else {// otherwise, if the queue's first message execution time is 10 s, now is 5 s, after when-now = 5s, ask nextPollTimeoutMillis = (int) Math. min (when-now, Integer. MAX_VALUE) ;}} else {// No message in the message queue nextPollTimeoutMillis =-1 ;}}}}
The key part here is the nativePollOnce local method, which is implemented in android_ OS _MessageQueue.cpp:
Static void android_ OS _MessageQueue_nativePollOnce (JNIEnv * env, jobject obj, jint ptr, jint timeoutMillis) {// Where ptr is the address of the NativeMessageQueue object, timeOutMills is the time after which NativeMessageQueue * nativeMessageQueue = reinterpret_cast <NativeMessageQueue *> (ptr); nativeMessageQueue-> pollOnce (timeoutMillis );}
We can see that the pollOnce method of calling NativeMessageQueue
void NativeMessageQueue::pollOnce(int timeoutMillis) { mLooper->pollOnce(timeoutMillis);}
Call the pollOnce method of logoff again:
inline int pollOnce(int timeoutMillis) { return pollOnce(timeoutMillis, NULL, NULL, NULL); }
Int LOCE: pollOnce (int timeoutMillis, int * outFd, int * outEvents, void ** outData) {// timeoutMillis, NULL parameter int result = 0; for (;) {while (mResponseIndex <mResponses. size () {const Response & response = mResponses. itemAt (mResponseIndex ++); ALooper_callbackFunc callback = response. request. callback; if (! Callback) {int ident = response. request. ident; int fd = response. request. fd; int events = response. events; void * data = response. request. data; if (outFd! = NULL) * outFd = fd; if (outEvents! = NULL) * outEvents = events; if (outData! = NULL) * outData = data; return ident ;}} if (result! = 0) {if (outFd! = NULL) * outFd = 0; if (outEvents! = NULL) * outEvents = NULL; if (outData! = NULL) * outData = NULL; return result;} // execute the pollInner Method result = pollInner (timeoutMillis );}}
The pollInner method is further called:
Int lomill:: pollInner (int timeoutMillis) {// Adjust the timeout based on when the next message is due. if (timeoutMillis! = 0 & mNextMessageUptime! = LLONG_MAX) {nsecs_t now = systemTime (SYSTEM_TIME_MONOTONIC); int messageTimeoutMillis = toMillisecondTimeoutDelay (now, mNextMessageUptime ); if (messageTimeoutMillis> = 0 & (timeoutMillis <0 | messageTimeoutMillis <timeoutMillis) {timeoutMillis = messageTimeoutMillis;} int result = ALOOPER_POLL_WAKE; mResponses. clear (); mResponseIndex = 0; // Wait for wakeAndLock () waiters to run then set MPolling to true. mLock. lock (); while (mWaiters! = 0) {mResume. wait (mLock);} mPolling = true; mLock. unlock (); size_t requestedCount = mRequestedFds. size (); int eventCount = poll (mRequestedFds. editArray (), requestedCount, timeoutMillis); // monitors the time on mRequestedFds for (int I = 0; I <eventCount; I ++) {int fd = eventItems [I]. data. fd; uint32_t epollEvents = eventItems [I]. events; if (fd = mWakeReadPipeFd) {if (epollEvents & EPOLLIN) {awoken (); // call the awoken method} else {....}} else {.....}} done :;... return result ;}
The awoken method called here:
Void loid: awoken () {char buffer [16]; ssize_t nRead; do {// read the "W" nRead = read (mWakeReadPipeFd, buffer, sizeof (buffer);} while (nRead =-1 & errno = EINTR) | nRead = sizeof (buffer ));}
7.3.2 msg.tar get. dispatchMessage (msg) send messages separatelySee the implementation of the Handler mechanism for this part.
7.4 AsyncTaskFor this part, see AsyncTask source code analysis.