自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Framework學(xué)習(xí)之input觸摸事件原理

移動(dòng)開發(fā) Android
對(duì)于觸摸事件會(huì)首先通過findTouchedWindowTargetsLocked找到目標(biāo)Window,進(jìn)而通過dispatchEventLocked將消息發(fā)送到目標(biāo)窗口。

這幾天修改input驅(qū)動(dòng),InputManagerService過程介紹下:

1、輸入驅(qū)動(dòng)系統(tǒng)簡(jiǎn)單介紹

  • Android設(shè)備可以同時(shí)連接多個(gè)輸入設(shè)備,比如說觸摸屏,鍵盤,鼠標(biāo)等等;
  • 用戶在任何一個(gè)設(shè)備上的輸入就會(huì)產(chǎn)生一個(gè)中斷,經(jīng)由Linux內(nèi)核的中斷處理以及設(shè)備驅(qū)動(dòng)轉(zhuǎn)換成一個(gè)Event,并傳遞給用戶空間的應(yīng)用程序進(jìn)行處理;
  • 每個(gè)輸入設(shè)備都有自己的驅(qū)動(dòng)程序,數(shù)據(jù)接口也不盡相同,如何在一個(gè)線程里(上面說過只有一個(gè)InputReader Thread)把所有的用戶輸入都給捕捉到? 這首先要?dú)w功于Linux 內(nèi)核的輸入子系統(tǒng)(Input Subsystem);
  •  它在各種各樣的設(shè)備驅(qū)動(dòng)程序上加了一個(gè)抽象層,只要底層的設(shè)備驅(qū)動(dòng)程序按照這層抽象接口來實(shí)現(xiàn),上層應(yīng)用就可以通過統(tǒng)一的接口來訪問所有的輸入設(shè)備;
  • 這個(gè)抽象層有三個(gè)重要的概念,input handler, input handle 和 input_dev,

圖片

  • input_dev代表底層驅(qū)動(dòng)
  •  input_handler代表某類輸入設(shè)備的處理方法,相當(dāng)于一個(gè)上層驅(qū)動(dòng)
  • 一個(gè)input_dev 可以有多個(gè)input_handler,同樣,一個(gè)input_handler 可以用于多種輸入設(shè)備;
  • 用來關(guān)聯(lián)某個(gè)input_dev 和 某個(gè) input_handler, 它對(duì)應(yīng)上圖中的紫色的原點(diǎn)。每個(gè)input handle 都會(huì)生成一個(gè)文件節(jié)點(diǎn);

input $ ls                                                        
event0 event1 event2 event3 event4 event5 event6

通過Linux input system獲取用戶輸入的流程簡(jiǎn)單如下:

  • 設(shè)備通過input_register_dev 將自己的驅(qū)動(dòng)注冊(cè)到Input 系統(tǒng)。
  • 各種Handler 通過 input_register_handler將自己注冊(cè)到Input系統(tǒng)中。
  • 每一個(gè)注冊(cè)進(jìn)來的input_dev 或 Input_handler 都會(huì)通過input_connect() 尋找對(duì)方,生成對(duì)應(yīng)的 input_handle,并在/dev/input/下產(chǎn)成一個(gè)設(shè)備節(jié)點(diǎn)文件.
  • 應(yīng)用程序通過打開(Open)Input_handle對(duì)應(yīng)的文件節(jié)點(diǎn),打開其對(duì)應(yīng)的input_dev 和 input_handler的驅(qū)動(dòng)。這樣,當(dāng)用戶按鍵時(shí),底層驅(qū)動(dòng)就能捕捉到,并交給對(duì)應(yīng)的上層驅(qū)動(dòng)(handler)進(jìn)行處理,然后返回給應(yīng)用程序。

2、InputManagerService詳解

2.1、InputManagerService啟動(dòng)

InputManagerService是Android為了處理各種用戶操作而抽象的一個(gè)服務(wù),自身可以看做是一個(gè)Binder服務(wù)實(shí)體,在SystemServer進(jìn)程啟動(dòng)的時(shí)候?qū)嵗?,并注?cè)到ServiceManager中去,不過這個(gè)服務(wù)對(duì)外主要是用來提供一些輸入設(shè)備的信息的作用,作為Binder服務(wù)的作用比較小

private void startOtherServices() {
...
inputManager = new InputManagerService(context);
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
...
}

InputManagerService跟WindowManagerService幾乎同時(shí)被添加,從一定程度上也能說明兩者幾乎是相生的關(guān)系;

而觸摸事件的處理也確實(shí)同時(shí)涉及兩個(gè)服務(wù),最好的證據(jù)就是WindowManagerService需要直接握著InputManagerService的引用;

如果對(duì)照上面的處理模型,InputManagerService主要負(fù)責(zé)觸摸事件的采集;

而WindowManagerService負(fù)責(zé)找到目標(biāo)窗口。接下來,先看看InputManagerService如何完成觸摸事件的采集;

2.2、如何捕獲觸摸事件

InputManagerService會(huì)單獨(dú)開一個(gè)線程專門用來讀取觸摸事件,

NativeInputManager::NativeInputManager(jobject contextObj,
jobject serviceObj, const sp<Looper>& looper) :
mLooper(looper), mInteractive(true) {
...
sp<EventHub> eventHub = new EventHub();
mInputManager = new InputManager(eventHub, this, this);
}

  • 這里有個(gè)EventHub,它主要是利用Linux的inotify和epoll機(jī)制;
  • 監(jiān)聽設(shè)備事件:包括設(shè)備插拔及各種觸摸、按鈕事件等,可以看做是一個(gè)不同設(shè)備的集線器,主要面向的是/dev/input目錄下的設(shè)備節(jié)點(diǎn),比如說/dev/input/event0上的事件就是輸入事件,通過EventHub的getEvents就可以監(jiān)聽并獲取該事件:

圖片

在new InputManager時(shí)候,會(huì)新建一個(gè)InputReader對(duì)象及InputReaderThread Loop線程,這個(gè)loop線程的主要作用就是通過EventHub的getEvents獲取Input事件

圖片

InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
<!--事件分發(fā)執(zhí)行類-->
mDispatcher = new InputDispatcher(dispatcherPolicy);
<!--事件讀取執(zhí)行類-->
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
void InputManager::initialize() {
mReaderThread = new InputReaderThread(mReader);
mDispatcherThread = new InputDispatcherThread(mDispatcher);
}
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
return true;
}
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
Vector<InputDeviceInfo> inputDevices;
{
...<!--監(jiān)聽事件-->
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
....<!--處理事件-->
processEventsLocked(mEventBuffer, count);
...
<!--通知派發(fā)-->
mQueuedListener->flush();
}

輸入事件就可以被讀取,經(jīng)過processEventsLocked被初步封裝成RawEvent,最后發(fā)通知,請(qǐng)求派發(fā)消息;

2.3、事件的派發(fā)

  • 在新建InputManager的時(shí)候,不僅僅創(chuàng)建了一個(gè)事件讀取線程;
  • 還創(chuàng)建了一個(gè)事件派發(fā)線程,雖然也可以直接在讀取線程中派發(fā),但是這樣肯定會(huì)增加耗時(shí),不利于事件的及時(shí)讀??;
  • 因此,事件讀取完畢后,直接向派發(fā)線程發(fā)個(gè)通知,請(qǐng)派發(fā)線程去處理,這樣讀取線程就可以更加敏捷,防止事件丟失,因此InputManager的模型就是如下樣式:

圖片

InputReader的mQueuedListener其實(shí)就是InputDispatcher對(duì)象,所以mQueuedListener->flush()就是通知InputDispatcher事件讀取完畢,可以派發(fā)事件了, InputDispatcherThread是一個(gè)典型Looper線程,基于native的Looper實(shí)現(xiàn)了Hanlder消息處理模型,如果有Input事件到來就被喚醒處理事件,處理完畢后繼續(xù)睡眠等待,代碼如下:

bool InputDispatcherThread::threadLoop() {
mDispatcher->dispatchOnce();
return true;
}
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{
<!--被喚醒 ,處理Input消息-->
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
...
}
nsecs_t currentTime = now();
int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
<!--睡眠等待input事件-->
mLooper->pollOnce(timeoutMillis);
}

以上就是派發(fā)線程的模型,dispatchOnceInnerLocked是具體的派發(fā)處理邏輯,這里看其中一個(gè)分支,觸摸事件:

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
...
case EventEntry::TYPE_MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
...
done = dispatchMotionLocked(currentTime, typedEntry,
&dropReason, nextWakeupTime);
break;
}
bool InputDispatcher::dispatchMotionLocked(
nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) {
...
Vector<InputTarget> inputTargets;
bool conflictingPointerActions = false;
int32_t injectionResult;
if (isPointerEvent) {
<!--關(guān)鍵點(diǎn)1 找到目標(biāo)Window-->
injectionResult = findTouchedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime, &conflictingPointerActions);
} else {
injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
}
...
<!--關(guān)鍵點(diǎn)2 派發(fā)-->
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}

從以上代碼可以看出,對(duì)于觸摸事件會(huì)首先通過findTouchedWindowTargetsLocked找到目標(biāo)Window,進(jìn)而通過dispatchEventLocked將消息發(fā)送到目標(biāo)窗口;

2.4、總結(jié)

現(xiàn)在把所有的流程跟模塊串聯(lián)起來,流程大致如下:

  • 點(diǎn)擊屏幕
  • InputManagerService的Read線程捕獲事件,預(yù)處理后發(fā)送給Dispatcher線程
  • Dispatcher找到目標(biāo)窗口
  • 通過Socket將事件發(fā)送到目標(biāo)窗口
  • 找到目標(biāo)窗口處理事件
責(zé)任編輯:武曉燕 來源: Android開發(fā)編程
相關(guān)推薦

2011-08-03 17:32:17

IOS UIScrollVi touch

2013-04-15 15:22:06

2021-08-11 14:29:20

鴻蒙HarmonyOS應(yīng)用

2016-12-08 22:59:47

觸摸事件android

2013-05-14 11:08:23

AIR Android觸摸事件鼠標(biāo)事件

2013-04-22 15:40:00

Android開發(fā)觸摸事件與點(diǎn)擊事件區(qū)別

2011-08-02 16:28:40

iPhone Web開發(fā) 事件

2017-01-11 18:44:43

React Nativ觸摸事件Android

2011-06-23 14:05:32

Qt 事件機(jī)制

2012-06-01 10:27:44

Cocos2d觸摸分發(fā)原理

2010-01-05 10:29:43

.NET Framew

2009-08-18 11:08:24

.Net Framew

2011-09-13 10:07:10

PhoneGap

2011-07-01 11:16:14

Struts

2017-12-21 15:42:08

iOS傳遞機(jī)制

2016-06-13 15:53:34

SDN開放網(wǎng)絡(luò)操作系統(tǒng)ONOS

2013-07-25 15:19:23

iOS開發(fā)學(xué)習(xí)Xcode打包framiOS開發(fā)

2023-10-12 22:44:16

iOS事件響應(yīng)鏈

2012-02-22 17:23:51

JavaPlay Framew

2013-09-17 14:00:19

AndroidListView原理
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)