前言
還得當年我剛接觸觸摸屏手機的時候纪隙,我就得非常好奇赊豌,為什么我觸摸屏幕會產(chǎn)生屏幕上UI的變化,感覺非常神奇绵咱。在進入這個行業(yè)之后碘饼,我才發(fā)現(xiàn)原來屏幕分觸控層和顯示層,我們觸摸屏幕的事件會通過"驅(qū)動-系統(tǒng)-應(yīng)用-應(yīng)用的某個UI控件"這一個完整流程悲伶。
Input子系統(tǒng)的流程圖
從圖中可以看到一次完整的事件傳遞包含兩個進程艾恼,system_server和app進程,我們這一篇先來分析一下左邊部分麸锉,也就是system_server钠绍。
知識準備-epoll
epoll有關(guān)的知識可以看我好友的博客Linux基礎(chǔ)知識之IO多路復(fù)用epoll
簡單解釋一下epoll的作用,類似于java中某個鎖的wait花沉,可以讓線程block柳爽,并不占用cpu纳寂,只是epoll可以監(jiān)聽多個fd的狀態(tài)并block,這個epoll wait之后泻拦,notify的條件是fd的內(nèi)容有變化。其實Looper就是基于epoll機制忽媒,有興趣可以看我好友博客Android P源碼分析之Looper(Native)
InputManagerService啟動
InputManagerService(初始化)
nativeInit
NativeInputManager
EventHub
InputManager
InputDispatcher
Looper
InputReader
QueuedInputListener
InputReaderThread
InputDispatcherThread
IMS.start(啟動)
nativeStart
InputManager.start
InputReaderThread->run
InputDispatcherThread->run
簡單理解就是system_server進程中的InputManagerService初始化運行以后争拐,會啟動兩個線程InputReader和InputDispatcher
Input事件的設(shè)備節(jié)點
我們可以通過adb shell getevent指令看到手機上所有的input事件的設(shè)備節(jié)點,驅(qū)動層會把從屏幕上采集到觸摸的事件寫到 /dev/input/event1這個設(shè)備節(jié)點晦雨,其他設(shè)備節(jié)點用于處理其他事件架曹,例如按鍵,搖桿闹瞧。
kobewang@KobedeMacBook-Pro:~$ adb shell getevent
add device 1: /dev/input/event5
name: "sm6150-t1-snd-card Button Jack"
add device 2: /dev/input/event4
name: "sm6150-t1-snd-card Headset Jack"
add device 3: /dev/input/event3
name: "ff_key"
add device 4: /dev/input/event2
name: "gpio-keys"
add device 5: /dev/input/event0
name: "qpnp_pon"
add device 6: /dev/input/event1
name: "fts_ts"
InputReader的主要工作
InputReader通過調(diào)用EventHub的getEvents方法監(jiān)聽Input事件的設(shè)備節(jié)點绑雄,在getEvents方法中就會采用epoll機制進行監(jiān)聽,然后發(fā)送event給InputDispatcher奥邮。
void InputReader::loopOnce() {
...
//從EventHub讀取事件万牺,其中EVENT_BUFFER_SIZE = 256
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{ // acquire lock
AutoMutex _l(mLock);
mReaderIsAliveCondition.broadcast();
if (count) { //處理事件
processEventsLocked(mEventBuffer, count);
}
if (oldGeneration != mGeneration) {
inputDevicesChanged = true;
getInputDevicesLocked(inputDevices);
}
...
} // release lock
//發(fā)送事件到nputDispatcher
mQueuedListener->flush();
}
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
AutoMutex _l(mLock); //加鎖
struct input_event readBuffer[bufferSize];
RawEvent* event = buffer; //原始事件
size_t capacity = bufferSize; //容量大小為256
bool awoken = false;
for (;;) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
...
bool deviceChanged = false;
while (mPendingEventIndex < mPendingEventCount) {
//從mPendingEventItems讀取事件項
const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
...
//獲取設(shè)備ID所對應(yīng)的device
ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
Device* device = mDevices.valueAt(deviceIndex);
if (eventItem.events & EPOLLIN) {
//從設(shè)備不斷讀取事件,放入到readBuffer
int32_t readSize = read(device->fd, readBuffer,
sizeof(struct input_event) * capacity);
if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
deviceChanged = true;
closeDeviceLocked(device);//設(shè)備已被移除則執(zhí)行關(guān)閉操作
} else if (readSize < 0) {
...
} else if ((readSize % sizeof(struct input_event)) != 0) {
...
} else {
int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
size_t count = size_t(readSize) / sizeof(struct input_event);
for (size_t i = 0; i < count; i++) {
//獲取readBuffer的數(shù)據(jù)
struct input_event& iev = readBuffer[i];
//將input_event信息, 封裝成RawEvent
event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
+ nsecs_t(iev.time.tv_usec) * 1000LL;
event->deviceId = deviceId;
event->type = iev.type;
event->code = iev.code;
event->value = iev.value;
event += 1;
capacity -= 1;
}
if (capacity == 0) {
mPendingEventIndex -= 1;
break;
}
}
}
...
}
...
mLock.unlock(); //poll之前先釋放鎖
//利用epoll機制監(jiān)聽input設(shè)備節(jié)點洽腺,等待input事件的到來
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
...
mLock.lock(); //poll之后再次請求鎖
...
}
return event - buffer; //返回所讀取的事件個數(shù)
}
InputDispatcher的主要工作
InputDispatcher接收來自InputReader的輸入事件脚粟,并記錄WMS的窗口信息,用于派發(fā)事件到合適的窗口蘸朋。具體的代碼我就不貼了核无,可以看一下[015]ANR視角看InputDispatcher來理解一下InputDispatcher。
總結(jié)
一個event時間的傳遞的前半段旅程
第一步:驅(qū)動將屏幕的event寫到了/dev/input/event1
第二步:InputReader線程通過EventHub的getEvents方法獲得event事件藕坯,并通知InputDispatcher線程
第三步: InputDispatcher將這個event通過InputChannel跨進程發(fā)給App進程
InputChannel是個什么团南?
這個問題我們將會在[018]Input子系統(tǒng)-下篇中講解
參考文章
Input系統(tǒng)—啟動篇
Input系統(tǒng)—InputReader線程
Input系統(tǒng)—InputDispatcher線程