hi,同学们大家好!
这些天有学员再群里问起了Handler中有个数据监听相关问题,学员有的认为Handler数据传递是靠流传递,误认为是epoll中监听的fd进行传递的,这个其实有必要更正这个学员的一个观点:
1、handler的数据传递完全是在自己进程中,所以完全不需要什么fd的流来传递,就相当于一个全局变量一样,你需要通过什么socket什么来传递数据么,你直接内存变量就可以访问到
2、那么学员就有疑问,那么handler looper说是基于epoll检测fd方式,epoll只是来监听回调通知作用,那么它监听的fd到底是个什么东西,如果不是流传递
这里我们就来揭开handler中looper的epoll监听的fd到底是什么类型的:
其实在我们android跨进程通讯实战课程中原来也讲解过Looper,其实Looper在java层就是一个简单的包装,真正干活在Loop.cpp中,而且这个Looper.cpp不在framework路径下面。
其实实在system/core路径下面:
test@test-Lenovo:~/xiaomi5/system/core$ find -name Loop*
./libutils/Looper.cpp
./libutils/Looper_test.cpp
./libutils/include/utils/Looper.h
下面我们从Loop.java开始一步步追:
public static void loop() {
//省略部分
for (;;) {
Message msg = queue.next(); // might block
//省略部分
}
}
可以看出loop是一个死循环在调用queue.next(),这样来一直源源不断取消息:
来看看queue.next():
frameworks/base/core/java/android/os/MessageQueue.java
Message next() {
//省略部分
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
//省略部分
}
}
这里调用是nativePollOnce,是一个native方法,即到达了native,来看看nativePollOnce方法:
frameworks/base/core/jni/android_os_MessageQueue.cpp
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
这里调用是NativeMessageQueue的pollOnce,NativeMessageQueue本身也在android_os_MessageQueue类中,这里来看一下:
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
这里大家看一下调用了是 mLooper->pollOnce(timeoutMillis),看到了开头说的Loop.cpp,它是在system/core/libutils目录:
system/core/libutils/include/utils/Looper.h
int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);
}
这里调用是int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)方法:
nt Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
//省略
result = pollInner(timeoutMillis);
}
}
这里又调用了pollInner:
int Looper::pollInner(int timeoutMillis) {
//省略部分
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
//省略部分
return result;
}
这里大家是不是看到一个非常非常熟悉的epoll_wait,即它就是没有消息时候阻塞,有消息时候唤醒的核心,也是handler是epoll的关键体现就是它,那么问题是它到达监听是哪个fd的变化呢?
其实本质上唤醒的fd是mWakeEventFd,而这个又是由谁进行赋值呢?只要找到他的赋值就可以看出他是个什么样的fd:
Looper::Looper(bool allowNonCallbacks)
: mAllowNonCallbacks(allowNonCallbacks),
mSendingMessage(false),
mPolling(false),
mEpollRebuildRequired(false),
mNextRequestSeq(0),
mResponseIndex(0),
mNextMessageUptime(LLONG_MAX) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));//这里就是对mWakeEventFd进行赋值
LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, "Could not make wake event fd: %s", strerror(errno));
AutoMutex _l(mLock);
rebuildEpollLocked();
}
可以看出mWakeEventFd本质是通过eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC),那么eventfd又是个什么呢?
这里我们Ubuntu使用man来看看它是什么:
VENTFD(2) Linux Programmer's Manual EVENTFD(2)
NAME
eventfd - create a file descriptor for event notification
SYNOPSIS
#include
int eventfd(unsigned int initval, int flags);
DESCRIPTION
eventfd() creates an "eventfd object" that can be used as an event wait/notify mechanism by user-space applications, and by the kernel to notify user-space applications of events. The ob‐
ject contains an unsigned 64-bit integer (uint64_t) counter that is maintained by the kernel. This counter is initialized with the value specified in the argument initval.
As its return value, eventfd() returns a new file descriptor that can be used to refer to the eventfd object.
The following values may be bitwise ORed in flags to change the behavior of eventfd():
EFD_CLOEXEC (since Linux 2.6.27)
Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful.
EFD_NONBLOCK (since Linux 2.6.27)
Set the O_NONBLOCK file status flag on the open file description (see open(2)) referred to by the new file descriptor. Using this flag saves extra calls to fcntl(2) to achieve the
same result.
EFD_SEMAPHORE (since Linux 2.6.30)
Provide semaphore-like semantics for reads from the new file descriptor. See below.
In Linux up to version 2.6.26, the flags argument is unused, and must be specified as zero.
The following operations can be performed on the file descriptor returned by eventfd():
read(2)
Each successful read(2) returns an 8-byte integer. A read(2) fails with the error EINVAL if the size of the supplied buffer is less than 8 bytes.
The value returned by read(2) is in host byte order—that is, the native byte order for integers on the host machine.
The semantics of read(2) depend on whether the eventfd counter currently has a nonzero value and whether the EFD_SEMAPHORE flag was specified when creating the eventfd file descrip‐
tor:
* If EFD_SEMAPHORE was not specified and the eventfd counter has a nonzero value, then a read(2) returns 8 bytes containing that value, and the counter' s value is reset to zero.
* If EFD_SEMAPHORE was specified and the eventfd counter has a nonzero value, then a read(2) returns 8 bytes containing the value 1, and the counter's value is decremented by 1.
* If the eventfd counter is zero at the time of the call to read(2), then the call either blocks until the counter becomes nonzero (at which time, the read(2) proceeds as described
above) or fails with the error EAGAIN if the file descriptor has been made nonblocking.
write(2)
A write(2) call adds the 8-byte integer value supplied in its buffer to the counter. The maximum value that may be stored in the counter is the largest unsigned 64-bit value minus 1
(i.e., 0xfffffffffffffffe). If the addition would cause the counter's value to exceed the maximum, then the write(2) either blocks until a read(2) is performed on the file descrip‐
tor, or fails with the error EAGAIN if the file descriptor has been made nonblocking.
A write(2) fails with the error EINVAL if the size of the supplied buffer is less than 8 bytes, or if an attempt is made to write the value 0xffffffffffffffff.
大致就是说:
eventfd - create a file descriptor for event notification
即专门用来创建事件通知文件描述符fd的一个方法,即他仅仅作用就是通知事件这种,不是平常见到的socket fd那种
好了到这里基本上解密了handler的fd到底是什么fd,它既不是 大家想的那种socket流fd,管道流fd,就是linux上专门系统方法evnetfd创建的事件通知fd
如果需要学习更多framework只是和视频可以b站关注:千里马学框架,购买课程最好加入422901085群里找千里马要优惠和答疑
课程答疑和新课信息:QQ交流群:422901085进行课程讨论,加群主qq享受 优惠
FrameWork入门课视频链接:https://edu.csdn.net/course/detail/30298
FrameWork实战课1视频链接:https://edu.csdn.net/course/detail/30275
FrameWork跨进程通信视频链接:https://edu.csdn.net/course/detail/35911
专题博客系列:
Android 8.1 zygote 启动过程源码
Android Framework实战视频–Zygote的fork进程篇
Android Framework实战视频–SystemServer启动篇
Android Framework实战视频–SystemServer启动FallbackHome篇
Android Framework实战视频–FallbackHome进程启动及Activity启动篇
Android Framework实战视频–FallbackHome结束启动Launcher篇
Android Framework实战视频–BootAnimation的启动源码分析(Android8.1)
Android Framework实战视频–init进程的bootanimation启动源码分析(补充Android 10部分的BootAnimation的启动源码分析)