Native logoff Analysis

Source: Internet
Author: User
Tags epoll

Logoff is a very important concept in Android. It is the main communication mode between Android Application threads, and it is also the main method for internal thread serialization, the core of logoff is actually a message queue. It completes inter-thread communication and intra-thread serialization operations by constantly processing messages in the logoff message queue. If any thread wants to use a message mechanism-specific operation, it must create a looper in the thread. How to Use the looper on the Java end is not described. Anyone who has Android development experience knows how to use it, this article mainly introduces native Looper and describes how it works with Java-layer looper to implement the most important thread communication mechanism in Android. At the same time, this article aims to explain the native
How is logoff used to implement pipeline communication.

As mentioned above, the core of Java logoff is actually a message queue. Let's analyze the logoff. java code does not have any data structures and operations associated with native, so the only thing that can be associated with native logoff is the messagequeue of the Java Message Queue type, define a member in messagequeue

    private int mPtr; // used by native code

It stores the address of the corresponding native Message Queue instance, and saves the native instance with an int type member, which is commonly used in JNI development. So messagequeue also uses mptr to represent native message queue, NativeMessageQueue@android_ OS _MessageQueue.cpp, look at the nativemessagequeue constructor, which defines a native logoff,

NativeMessageQueue::NativeMessageQueue() {    mLooper = Looper::getForThread();    if (mLooper == NULL) {        mLooper = new Looper(false);        Looper::setForThread(mLooper);    }}

Therefore, the entire structure is very simple. Java logoff contains a messagequeue. The native instance corresponding to messagequeue is a nativemessagequeue instance, and nativemessagequeue generates a native Looper when it is created.

In fact, native logoff is used as a toggle of the Java logoff mechanism,

1. When a message is stored in a message queue, wake up natvice loice. As to how to send a message to a thread, handler is used. The Google documentation details the message;

2. When there is no message in the message queue or the message has not reached the processing time, natvice loice blocks the entire thread.

The above function can be summarized in one sentence: the thread that creates a Java logoff is active only when the message is waiting for processing, and the block is waiting for the message to be written when there is no message.
Next we will analyze how natvice loice implements this function. We will first start with natvice loice to see what it can do.

1. Native logoff Initialization

Let's take a look at all the work done in the initialization process of natvice loice.

1. Create a pipe pipeline mwakereadpipefd and mwakewritepipefd, which is used to wake up the blocked thread.

2. create an epoll instance mepollfd and use it to listen for event triggering. The event has a wake event on mwakereadpipefd, And the nativeinputqueue and inputdispatcher modules mentioned in the previous article are registered in their respective lofd, the notification event of the hardware device event to be monitored and the notification event after the event is digested.

In the constructor, only the listener for mwakereadpipefd is added to mepollfd. The Listener registered by the nativeinputqueue and inputdispatcher modules must be registered in their respective modules using the addfd () method, currently, only nativeinputqueue actually uses the native loative of the current thread, and inputdispatcher is a custom native Locher. For more information, see the previous article.

Looper::Looper(bool allowNonCallbacks) :        mAllowNonCallbacks(allowNonCallbacks),        mResponseIndex(0) {    int wakeFds[2];    int result = pipe(wakeFds);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);    mWakeReadPipeFd = wakeFds[0];    mWakeWritePipeFd = wakeFds[1];    result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",            errno);    result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",            errno);#ifdef LOOPER_USES_EPOLL    // Allocate the epoll instance and register the wake pipe.    mEpollFd = epoll_create(EPOLL_SIZE_HINT);    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);    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);    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",            errno);#else    ........................................... }

2. Native logoff business logic

In this section, we will analyze the native logoff as the switch controller of the Java logoff mechanism.

Java logoff continuously checks whether messages in the message queue need to be processed by calling loop (). The next () method of messagequeue is called, and the message stored in messagequeue is returned.

Loop () @ lofter. Java

   Message msg = queue.next(); // might block

    final Message next() {        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            nativePollOnce(mPtr, nextPollTimeoutMillis);            synchronized (this) {                // Try to retrieve the next message.  Return if found.                final long now = SystemClock.uptimeMillis();                final Message msg = mMessages;                if (msg != null) {                    final long when = msg.when;                    if (now >= when) {                        mBlocked = false;                        mMessages = msg.next;                        msg.next = null;                        if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);                        return msg;                    } else {                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);                    }                } else {                    nextPollTimeoutMillis = -1;                }                // If first time, then get the number of idlers to run.                if (pendingIdleHandlerCount < 0) {                    pendingIdleHandlerCount = mIdleHandlers.size();                }                if (pendingIdleHandlerCount == 0) {                    // No idle handlers to run.  Loop and wait some more.                    mBlocked = true;                    continue;                }                if (mPendingIdleHandlers == null) {                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];                }                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);            }            // Run the idle handlers.            // We only ever reach this code block during the first iteration.            for (int i = 0; i < pendingIdleHandlerCount; i++) {                final IdleHandler idler = mPendingIdleHandlers[i];                mPendingIdleHandlers[i] = null; // release the reference to the handler                boolean keep = false;                try {                    keep = idler.queueIdle();                } catch (Throwable t) {                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);                }                if (!keep) {                    synchronized (this) {                        mIdleHandlers.remove(idler);                    }                }            }            // Reset the idle handler count to 0 so we do not run them again.            pendingIdleHandlerCount = 0;            // While calling an idle handler, a new message could have been delivered            // so go back and look again for a pending message without waiting.            nextPollTimeoutMillis = 0;        }    }

In the process of obtaining messagequeue messages, we found that next () is a loop. In addition to obtaining message queues, the most important thing is to listen to the event trigger of natvie lovie, call nativepollonce (mptr, nextpolltimeoutmillis); it will eventually call pollinner () @ loner. CPP. Let's see what pollinner has done?

1. wait for the mepollfd event to trigger. We have mentioned that there are two types of event triggers: the first is to wake up the wake message of native logoff, and the other is to reuse other messages of native logoff, for example, nativeinputqueue and inputdispatcher pipeline detection (Android is also used by these two modules ).

When the epoll_wait () wait time is not 0, that is, the nextpolltimeoutmillis value passed by Java logoff, the entire thread will be blocked here.

Pollinner () @ low.cpp

    struct epoll_event eventItems[EPOLL_MAX_EVENTS];    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);    bool acquiredLock = false;

2. if an event is triggered, wake or other logoff events are used to process the event. In this way, the entire native logoff is freed from the block state, and the entire thread is freed, java logoff will execute nativepollonce (mptr, nextpolltimeoutmillis); the following statement, next () @ messagequeue. the statements following Java are not explained.

We only need to pay attention to the setting of the nextpolltimeoutmillis value. If the message has not reached the processing time, the nextpolltimeoutmillis value is the total duration from the message processing time, it indicates that native logoff only needs to block the time required for message processing. If no message is waiting for processing, the native logoff will remain blocked and wait for the wake event.

So when will a wake event happen? Only when new messages are stored in messagequeue will wake events be initiated to native logoff.

Enqueuemessage () @ messagequeue. Java

        if (needWake) {            nativeWake(mPtr);        }

The basic mechanism of the entire native logoff is as follows, which ensures that the CPU usage can be minimized when no message is available for processing in the thread, and valuable CPU resources can be handed over to other threads or processes for processing, this is important for mobile devices.

3. the native logoff extension application native logoff, although usually used in combination with the logoff and messagequeue at the Java layer, exists as the switch controller of Java logoff, however, given the above analysis of native logoff, we found that during native code development, we can also use native logoff independently, for example, if pipe or socket communication is used in the developed native code, native logoff will be a powerful tool through which we can well manage pipe or socket communication. As inputdispatcher does.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.