current position:Home>Familiar and unfamiliar handler-3
Familiar and unfamiliar handler-3
2022-01-27 03:51:04 【AaronOhOhOh】
This is my participation 8 The fourth of the yuegengwen challenge 5 God , Check out the activity details :8 Yuegengwen challenge
Familiar and strange Handler-3
Continued above :
Familiar and strange Handler-1
Familiar and strange Handler-2
nativeInit:
In the above , About Handler Three piece set creation process , The first involves JNI The call is MessageQueue Of nativeInit Method .
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
Copy code
The concrete realization is framework/base/core/jni/android_os_MessageQueue.cpp in :
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
// First create NativeMessageQueue object , The object holds Native On the side Looper object
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
// If the creation fails , Direct anomaly
if (!nativeMessageQueue) {
jniThrowRuntimeException(env, "Unable to allocate native queue");
return 0;
}
// increase NativeMessageQueue Reference count of
nativeMessageQueue->incStrong(env);
// return nativeMessageQueue This pointer is for Java layer
return reinterpret_cast<jlong>(nativeMessageQueue);
}
Copy code
stay NativeMessageQueue In the constructor of :
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
// adopt TLS(Thread Local Storage Thread local storage ) Gets the current thread's Native Of Looper object
mLooper = Looper::getForThread();
if (mLooper == NULL) {
// without , Then I will create a Native Of Looper object
mLooper = new Looper(false);
// And will create Looper Object to save to TLS in
Looper::setForThread(mLooper);
}
}
Copy code
You need to pay attention to the Looper Objects and Java Layer of Looper Objects are not directly related . In its construction method , The two most relevant things :
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(0),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
// Initialize the file descriptor representing the wake-up event mWakeEventFd
// eventfd This system call is used to create or open a eventfd The file of , Like a document open operation
// The initial value passed in here is 0, Then set the flag bit to
// EFD_CLOEXEC:FD_CLOEXEC, In short, it is fork Child processes do not inherit , There is nothing wrong with setting this value for multithreaded programs .
// EFD_NONBLOCK:
// The file will be set to O_NONBLOCK( Non blocking IO, When the data cannot be read or the write buffer is full return -1),
// Instead of blocking calls , Generally, you need to set .
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
AutoMutex _l(mLock);
// Recreate the current Looper Of Epoll event
rebuildEpollLocked();
}
Copy code
rebuildEpollLocked The implementation is as follows :
void Looper::rebuildEpollLocked() {
// If at present Looper Already there. EpollFd, That is, there are old epoll example , Then reset it first
if (mEpollFd >= 0) {
mEpollFd.reset();
}
// Create a new Epoll example
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
struct epoll_event eventItem;
// initialization eventItem Occupied memory space
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
// EPOLLIN : Indicates that the corresponding file descriptor can be read
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeEventFd.get();
// call epoll_ctl operation mEpollFd Corresponding Epoll example , take mWakeEventFd( Wake Events )
// Add to mEpoll Corresponding epoll For instance
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
// take Request Also added to epoll For instance
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",
request.fd, strerror(errno));
}
}
}
Copy code
epoll_event The structure is as follows :
struct epoll_event {
uint32_t events;
epoll_data_t data;
}
typedef union epoll_data {
void* ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
Copy code
events Member variables : It can be a collection of the following macros :
- EPOLLIN : Indicates that the corresponding file descriptor can be read ( Including the end SOCKET Normally shut down );
- EPOLLOUT: Indicates that the corresponding file descriptor can write ;
- EPOLLPRI: Indicates that the corresponding file descriptor has urgent data readability ( This should indicate that there is out-of-band data coming in );
- EPOLLERR: Indicates that there is an error in the corresponding file descriptor ;
- EPOLLHUP: Indicates that the corresponding file descriptor is suspended ;
- EPOLLET: take EPOLL Set edge trigger (Edge Triggered) Pattern , This is relative to the horizontal trigger (Level Triggered) Speaking of .
- EPOLLONESHOT: Listen for only one event , After listening to this event , If you need to keep listening to this socket Words , I have to do this again socket Add to EPOLL In the queue .
since Java Layer of MessageQueue At creation time , Created Native Layer of MessageQueue, So the same Java layer MQ At the time of destruction , It will also trigger NativeMessageQueue Destruction of ,Native layer MQ The destruction of is relatively simple , In essence Native Clear an object in the layer :
- Remove the reference relationship of the object .
- delete Call to clear the memory space of the object .
nativeWake:
From the above analysis , We know , stay Java Layer when we call MessageQueue.enqueueMessage When , stay Java When the layer feels the need to wake up the message queue , Would call nativeWake This native Method :
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
// call NativeMessageQueue Of wake Method .
nativeMessageQueue->wake();
}
Copy code
NativeMessageQueue Of wake The way is to adjust Native Looper Of wake Method :
void Looper::wake() {
uint64_t inc = 1;
// To the pipe mWakeEventFd Write characters 1
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
Copy code
TEMP_FAILURE_RETRY The function of this macro expression is , Evaluate the passed in expression , When the passed in expression evaluates to -1, It means failure , When the expression returns a value -1 And the error code is set to EINITR(4), Then he'll keep trying , Until success .
nativePollOnce:
From the above analysis , We know , stay Java Layer message queue processing Message Before , Will call first nativePollOnce, Handle Native Layer messages :
nativePollOnce(ptr, nextPollTimeoutMillis);
Copy code
ptr It was before Native Created by layer MessageQueue Of “ The pointer ”,nextPollTimeoutMillis Indicates the time when the next message will be taken out .
stay android_os_MessageQueue.cpp in ,nativePollOnce The implementation is as follows :
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
// take Java The layer passed mPtr Convert to NativeMessageQueue The pointer
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
// call nativeMessageQueue Of pollOnce Method
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
Copy code
pollOnce:
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
// ...
// Call to Looper Of pollOnce Method
mLooper->pollOnce(timeoutMillis);
// ...
}
Copy code
Looper Of pollOnce The ultimate realization is system/core/libutils/Looper.cpp:
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
// First deal with Native Layer of Response
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++)
// When ident>=0 When , It means that there is no callback
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
if (outFd != nullptr) *outFd = fd;
if (outEvents != nullptr) *outEvents = events;
if (outData != nullptr) *outData = data;
return ident;
}
}
// If there is result, Then quit
if (result != 0) {
if (outFd != nullptr) *outFd = 0;
if (outEvents != nullptr) *outEvents = 0;
if (outData != nullptr) *outData = nullptr;
return result;
}
// call pollInner
result = pollInner(timeoutMillis);
}
}
Copy code
Response and Request The structure of is as follows :
struct Request {
// request Associated file descriptor
int fd;
// requestId, When it comes to POLL_CALLBACK(-2) When , Express callback
int ident;
int events;
int seq;
// request Processing callbacks for
sp<LooperCallback> callback;
void* data;
void initEventItem(struct epoll_event* eventItem) const;
};
struct Response {
int events;
Request request;
};
Copy code
Looper::pollInner The implementation is as follows , The internal first is to call epoll_wait This blocking method , obtain Epoll Number of events , Then according to this number ,
int Looper::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;
}
}
// Poll.
int result = POLL_WAKE;
// The above analysis is known , stay pollInner Before being called ,mResponses It's all handled
mResponses.clear();
mResponseIndex = 0;
// begin in a minute epoll polling .
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// wait for epoll_wait System call return , return timeoutMillis Time file descriptor mEpollFd What happened on epoll Number of events
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// End of poll , Let's start processing the received events .
mPolling = false;
// Acquire lock.
mLock.lock();
// For example, something unusual happened , Need to recreate Epoll Mechanism
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// When epoll The number of events is less than 0 When , That is, an exception is considered to have occurred , Jump to Done Continue to execute
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
result = POLL_ERROR;
goto Done;
}
// When epoll Event equals 0 When , Indicates that polling timed out , Jump directly to Done Continue to execute
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
// Start loop traversal , Deal with all event
for (int i = 0; i < eventCount; i++) {
// Get the of an event FD
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
// If it's a wake-up event
if (fd == mWakeEventFd.get()) {
if (epollEvents & EPOLLIN) {
// It has been awakened at this time , Read and empty the data in the pipeline
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
// Through file descriptors , Find the corresponding Request Indexes
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
// The corresponding request Encapsulated into Response Object and push To mRequests This sum Vector in
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
// Response All the events have been handled , Next processing Native Of Message event .
mNextMessageUptime = LLONG_MAX;
// mMessageEnvelopes It's a Vector,MessageEnvelopes As the name suggests, the message envelope
// Encapsulates the Message and MessageHandler object
// Message A message ,MessageHandler Defined a handleMessage Method
// By calling Looper::sendMessageXX You can send a message Native Message
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
// It's time for a message to be processed , Then remove and execute
// Corresponding MessageHandler Of handleMessage Method .
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
handler->handleMessage(message);
} // release handler
mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
// If the message at the head of the queue has not reached the time to be processed
// Then the queue needs to be suspended until the header message can be processed
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
// Then deal with the above push Come in mResponses, namely Request
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
// Yes callback
if (response.request.ident == POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
// perform callback
int callbackResult = response.request.callback->handleEvent(fd, events, data)
if (callbackResult == 0) {
removeFd(fd, response.request.seq);
}
// eliminate callback References to
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
Copy code
Whole Native layer Looper The top priority of the mechanism is Looper::pollInner Medium epoll_wait This system call , This call occurs when the message queue has no work to process , Will block the current thread , Release system resources , in other words ,Looper The dead cycle mechanism of does not always occupy system resources , When there are no tasks to deal with , The main thread is blocked , Therefore, it will not cause excessive resource occupation .
Or more understandable , What we see Looper.loop Opened a dead cycle , This dead cycle is indeed a dead cycle . But what's special is , Instead of writing an infinite loop ,CPU Will always carry out , Then it leads to a surge in resource occupancy ,Looper.loop This is a dead cycle , When there are no messages to process , It will stop blocking , No more epoll_wait Carry out later .
And we don't feel the main thread stop , Because , A code we wrote , Execution is passive , We are in the sub thread post One message,MessageQueue receive messages , Main thread Looper.loop Execute code , After executing the code, remove the next Message, No, Message, The main thread continues to block . The code we wrote executed , Of course, you can't feel the main thread blocking .
Summary :
Through to Native Of pollOnce Analysis of , We know Android Our message processing mechanism actually spans Java Layer and the Native Layer of .
Java Layer and the Native layer , Through some of the above JNI Invocation and mPtr This key pointer , take Java Layer of MessageQueue and Native Of MessageQueue Association , In this way Java When the message mechanism of layer works ,Native The message mechanism of layer can also work together .
The flow of message processing is to process Native Of Message, And then deal with Native Of Request, In the end pollOnce After that , Handle Java Layer of Message, So sometimes Java There are not many messages in the layer, but the response time is relatively slow. It may be Native The message mechanism of the layer leads to .
copyright notice
author[AaronOhOhOh],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270350549332.html
The sidebar is recommended
- Spring IOC container loading process
- [thinking] the difference between singleton mode and static method - object-oriented programming
- Hadoop environment setup (MySQL environment configuration)
- 10 minutes, using node JS creates a real-time early warning system for bad weather!
- Git tool
- Force deduction algorithm - 92 Reverse linked list II
- What is the sub problem of dynamic programming?
- C / C + +: static keyword summary
- Idea does not have the artifacts option when configuring Tomcat
- Anaconda can't open it
guess what you like
-
I don't know how to start this
-
Matlab simulation of transportation optimization algorithm based on PSO
-
MySQL slow log optimization
-
[Vue] as the window is stretched (larger, smaller, wider and higher), the text will not be displayed
-
Popular Linux distributions for embedded computing
-
Suzhou computer research
-
After installing SSL Certificate in Windows + tomcat, the domain name request is not successful. Please answer!!
-
Implementation time output and greetings of jQuery instance
-
The 72 year old uncle became popular. Wu Jing and Guo fan made his story into a film, which made countless dreamers blush
-
How to save computer research
Random recommended
- Springboot implements excel import and export, which is easy to use, and poi can be thrown away
- The final examination subjects of a class are mathematical programming, and the scores are sorted and output from high to low
- Two pronged approach, Tsinghua Professor Pro code JDK and hotspot source code notes, one-time learning to understand
- C + + recursive knapsack problem
- The use of GIT and GitHub and the latest git tutorial are easy to understand -- Video notes of crazy God speaking
- PostgreSQL statement query
- Ignition database test
- Context didn't understand why he got a high salary?, Nginxfair principle
- Bootstrap switch switch control user's guide, springcloud actual combat video
- A list that contains only strings. What other search methods can be used except sequential search
- [matlab path planning] multi ant colony algorithm grid map path planning [including GUI source code 650]
- [matlab path planning] improved genetic algorithm grid map path planning [including source code phase 525]
- Iinternet network path management system
- Appium settings app is not running after 5000ms
- Reactnative foundation - 07 (background image, status bar, statusbar)
- Reactnative foundation - 04 (custom rpx)
- If you want an embedded database (H2, hsql or Derby), please put it on the classpath
- When using stm32g070 Hal library, if you want to write to flash, you must perform an erase. If you don't let it, you can't write continuously.
- Linux checks where the software is installed and what files are installed
- SQL statement fuzzy query and time interval filtering
- 69. Sqrt (x) (c + + problem solving version with vs runnable source program)
- Fresh students are about to graduate. Do you choose Java development or big data?
- Java project: OA management system (java + SSM + bootstrap + MySQL + JSP)
- Titanic passenger survival prediction
- Vectorization of deep learning formula
- Configuration and use of private image warehouse of microservice architect docker
- Relearn JavaScript events
- For someone, delete return 1 and return 0
- How does Java dynamically obtain what type of data is passed? It is used to judge whether the data is the same, dynamic data type
- How does the database cow optimize SQL?
- [data structure] chain structure of binary tree (pre order traversal) (middle order traversal) (post order traversal) (sequence traversal)
- Webpack packaging optimization solution
- 5. Operation element
- Detailed explanation of red and black trees
- redhat7. 9 install database 19C
- Blue Bridge Cup notes: (the given elements are not repeated) complete arrangement (arrangement cannot be repeated, arrangement can be repeated)
- Detailed explanation of springboot default package scanning mechanism and @ componentscan specified scanning path
- How to solve the run-time exception of test times
- Detailed explanation of k8s management tool kubectl
- Android system view memory command