付費(fèi)下載
下載本文檔
版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android中怎么利用Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng)
今天就跟大家聊聊有關(guān)Android中怎么利用Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng),可能很多人都不太了解,為了讓大家更加了解,在下給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。InputManagerService初始化概覽首先,有幾點(diǎn)共識(shí)我們都可以達(dá)成:Android
Framework層的Service(Java)都是由system_server進(jìn)程創(chuàng)建的(由于沒(méi)有fork,因此都運(yùn)行在system_server進(jìn)程中)Service創(chuàng)建后就會(huì)交給運(yùn)行在system_server進(jìn)程中的ServiceManager管理。因此對(duì)于InputManagerService的創(chuàng)建,我們可以在SystemServer的startOtherServices()方法中找到,該方法做了以下事情:創(chuàng)建InputManagerService對(duì)象將它交給ServiceManager管理將WindowManagerService的InputMonitor注冊(cè)到InputManagerService中作為窗口響應(yīng)事件后的回調(diào)完成以上工作后啟動(dòng)InputManagerService。
SystemServer.javastartOtherServices(){
……
inputManager
=
new
InputManagerService(context);
……
inputManager.setWindowManagerCallbacks(wm.getInputMonitor());
inputManager.start();
……
}接下來(lái)我們就逐部分學(xué)習(xí)相應(yīng)的處理。InputManagerService對(duì)象的創(chuàng)建創(chuàng)建InputManagerService對(duì)象時(shí)會(huì)完成以下工作:創(chuàng)建一個(gè)負(fù)責(zé)處理DisplayThread線程中的Message的Handler調(diào)用nativeInit初始化native層的InputManagerService,初始化的時(shí)候傳入了DisplayThread的消息隊(duì)列用mPtr保存native層的InputManagerService初始化完成后將Service添加到LocalServices,通過(guò)Map以鍵值對(duì)的形式存儲(chǔ)
InputManagerService.javapublic
InputManagerService(Context
context)
{
this.mContext
=
context;
this.mHandler
=
new
InputManagerHandler(DisplayThread.get().getLooper());
mUseDevInputEventForAudioJack
=
context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
Slog.i(TAG,
"Initializing
input
manager,
mUseDevInputEventForAudioJack="
+
mUseDevInputEventForAudioJack);
mPtr
=
nativeInit(this,
mContext,
mHandler.getLooper().getQueue());
LocalServices.addService(InputManagerInternal.class,
new
LocalService());
}這里可能有人就會(huì)問(wèn)了,為什么InputManagerService要和DisplayThread綁定在一起?大家不妨想想,InputEvent無(wú)論如何被獲取、歸類、分發(fā),最終還是要被處理,也就意味著最終它的處理結(jié)果都要在UI上體現(xiàn),那么InputManagerService自然要選擇和UI親近一些的線程在一起了。但是問(wèn)題又來(lái)了,應(yīng)用都是運(yùn)行在自己的主線程里的,難道InputManagerService要一個(gè)個(gè)綁定么,還是一個(gè)個(gè)輪詢?這些做法都太過(guò)低效,那換個(gè)辦法,可不可以和某個(gè)管理或非常親近所有應(yīng)用UI的線程綁定在一起呢?答案是什么,我在這里先不說(shuō),大家可以利用自己的知識(shí)想想。初始化native層的InputManagerService在nativeInit函數(shù)中,將Java層的MessageQueue轉(zhuǎn)換為native層的MessageQueue,然后再取出Looper用于NativeInputManager的初始化??梢?jiàn)這里的重頭戲就是NativeInputManager的創(chuàng)建,這個(gè)過(guò)程做了以下事情:將Java層的Context和InputManagerService轉(zhuǎn)換為native層的Context和InputManagerService存儲(chǔ)在mContextObj和mServiceObj中初始化變量創(chuàng)建EventHub創(chuàng)建InputManager
com_android_server_input_InputManagerService.cpp
NativeInputManager::NativeInputManager(jobject
contextObj,
jobject
serviceObj,
const
sp<Looper>&
looper)
:
mLooper(looper),
mInteractive(true)
{
JNIEnv*
env
=
jniEnv();
mContextObj
=
env->NewGlobalRef(contextObj);
mServiceObj
=
env->NewGlobalRef(serviceObj);
{
AutoMutex
_l(mLock);
mLocked.systemUiVisibility
=
ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
mLocked.pointerSpeed
=
0;
mLocked.pointerGesturesEnabled
=
true;
mLocked.showTouches
=
false;
}
mInteractive
=
true;
sp<EventHub>
eventHub
=
new
EventHub();
mInputManager
=
new
InputManager(eventHub,
this,
this);
}EventHub看到這里很多人就會(huì)想,EventHub是什么?取英語(yǔ)釋義來(lái)看,它的意思是事件樞紐。我們?cè)谖恼麻_(kāi)頭的時(shí)候也提到過(guò),Input系統(tǒng)的事件來(lái)源于驅(qū)動(dòng)/內(nèi)核,那么我們可以猜測(cè)EventHub是處理來(lái)自驅(qū)動(dòng)/內(nèi)核的元事件的樞紐。接下來(lái)就在源碼中驗(yàn)證我們的想法吧。EventHub的創(chuàng)建過(guò)程中做了以下事情:創(chuàng)建mEpollFd用于監(jiān)聽(tīng)是否有數(shù)據(jù)(有無(wú)事件)可讀創(chuàng)建mINotifyFd將它注冊(cè)到DEVICE_PATH(這里路徑就是/dev/input)節(jié)點(diǎn),并將它交給內(nèi)核用于監(jiān)聽(tīng)該設(shè)備節(jié)點(diǎn)的增刪數(shù)據(jù)事件。那么只要有數(shù)據(jù)增刪的事件到來(lái),epoll_wait()就會(huì)返回,使得EventHub能收到來(lái)自系統(tǒng)的通知,并獲取事件的詳細(xì)信息調(diào)用epoll_ctl函數(shù)將mEpollFd和mINotifyFd注冊(cè)到epoll中定義intwakeFd[2]作為事件傳輸管道的讀寫兩端,并將讀端注冊(cè)到epoll中讓mEpollFd監(jiān)聽(tīng)
EventHub.cpp
EventHub::EventHub(void)
:
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD),
mNextDeviceId(1),
mControllerNumbers(),
mOpeningDevices(0),
mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),
mNeedToScanDevices(true),
mPendingEventCount(0),
mPendingEventIndex(0),
mPendingINotify(false)
{
acquire_wake_lock(PARTIAL_WAKE_LOCK,
WAKE_LOCK_ID);
mEpollFd
=
epoll_create(EPOLL_SIZE_HINT);
LOG_ALWAYS_FATAL_IF(mEpollFd
<
0,
"Could
not
create
epoll
instance.
errno=%d",
errno);
mINotifyFd
=
inotify_init();
int
result
=
inotify_add_watch(mINotifyFd,
DEVICE_PATH,
IN_DELETE
|
IN_CREATE);
……
result
=
epoll_ctl(mEpollFd,
EPOLL_CTL_ADD,
mINotifyFd,
&eventItem);
……
int
wakeFds[2];
result
=
pipe(wakeFds);
……
mWakeReadPipeFd
=
wakeFds[0];
mWakeWritePipeFd
=
wakeFds[1];
result
=
fcntl(mWakeReadPipeFd,
F_SETFL,
O_NONBLOCK);
……
result
=
fcntl(mWakeWritePipeFd,
F_SETFL,
O_NONBLOCK);
……
result
=
epoll_ctl(mEpollFd,
EPOLL_CTL_ADD,
mWakeReadPipeFd,
&eventItem);
……
}
那么這里拋出一個(gè)問(wèn)題:為什么要把管道的讀端注冊(cè)到epoll中?假如EventHub因?yàn)間etEvents讀不到事件而阻塞在epoll_wait()里,而我們沒(méi)有綁定讀端的話,我們要怎么喚醒EventHub?如果綁定了管道的讀端,我們就可以通過(guò)向管道的寫端寫數(shù)據(jù)從而讓EventHub因?yàn)榈玫焦艿缹懚说臄?shù)據(jù)而被喚醒。InputManager的創(chuàng)建接下來(lái)繼續(xù)說(shuō)InputManager的創(chuàng)建,它的創(chuàng)建就簡(jiǎn)單多了,創(chuàng)建一個(gè)InputDispatcher對(duì)象用于分發(fā)事件,一個(gè)InputReader對(duì)象用于讀事件并把事件交給InputDispatcher分發(fā),,然后調(diào)用initialize()初始化,其實(shí)也就是創(chuàng)建了InputReaderThread和InputDispatcherThread。InputManager.cpp
InputManager::InputManager(
const
sp<EventHubInterface>&
eventHub,
const
sp<InputReaderPolicyInterface>&
readerPolicy,
const
sp<InputDispatcherPolicyInterface>&
dispatcherPolicy)
{
mDispatcher
=
new
InputDispatcher(dispatcherPolicy);
mReader
=
new
InputReader(eventHub,
readerPolicy,
mDispatcher);
initialize();
}void
InputManager::initialize()
{
mReaderThread
=
new
InputReaderThread(mReader);
mDispatcherThread
=
new
InputDispatcherThread(mDispatcher);
}InputDispatcher和InputReader的創(chuàng)建都相對(duì)簡(jiǎn)單。InputDispatcher會(huì)創(chuàng)建自己線程的Looper,以及設(shè)置根據(jù)傳入的dispatchPolicy設(shè)置分發(fā)規(guī)則。InputReader則會(huì)將傳入的InputDispatcher封裝為監(jiān)聽(tīng)對(duì)象存起來(lái),做一些數(shù)據(jù)初始化就結(jié)束了。至此,InputManagerService對(duì)象的初始化就完成了,根據(jù)開(kāi)頭說(shuō)的,接下來(lái)就會(huì)調(diào)用InputManagerService的start()方法。監(jiān)聽(tīng)線程InputReader和InputDispatcher的啟動(dòng)在start()方法中,做了以下事情:調(diào)用nativeStart方法,其實(shí)就是調(diào)用InputManager的start()方法將InputManagerService交給WatchDog監(jiān)控注冊(cè)觸控點(diǎn)速度、顯示觸控的觀察者,并注冊(cè)廣播監(jiān)控它們主動(dòng)調(diào)用updateXXX方法更新(初始化)
InputManagerService.javapublic
void
start()
{
Slog.i(TAG,
"Starting
input
manager");
nativeStart(mPtr);
//
Add
ourself
to
the
Watchdog
monitors.
Watchdog.getInstance().addMonitor(this);
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
registerAccessibilityLargePointerSettingObserver();
mContext.registerReceiver(new
BroadcastReceiver()
{
@Override
public
void
onReceive(Context
context,
Intent
intent)
{
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}
},
new
IntentFilter(Intent.ACTION_USER_SWITCHED),
null,
mHandler);
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
updateAccessibilityLargePointerFromSettings();
}顯而易見(jiàn)這里最值得關(guān)注的就是InputManager的start()方法了,可惜這個(gè)方法并不值得我們?nèi)绱岁P(guān)心,因?yàn)樗龅氖虑楹芎?jiǎn)單,就是啟動(dòng)InputDispatcherThread和InputReaderThread開(kāi)始監(jiān)聽(tīng)。status_t
InputManager::start()
{
status_t
result
=
mDispatcherThread->run("InputDispatcher",
PRIORITY_URGENT_DISPLAY);
if
(result)
{
ALOGE("Could
not
start
InputDispatcher
thread
due
to
error
%d.",
result);
return
result;
}
result
=
mReaderThread->run("InputReader",
PRIORITY_URGENT_DISPLAY);
if
(result)
{
ALOGE("Could
not
start
InputReader
thread
due
to
error
%d.",
result);
mDispatcherThread->requestExit();
return
result;
}
return
OK;
}那么InputReaderThread線程是怎么和EventHub關(guān)聯(lián)起來(lái)的呢?對(duì)于InputReadThread:?jiǎn)?dòng)后循環(huán)執(zhí)行mReader->loopOnce()loopOnce()中會(huì)調(diào)用mEventHub->getEvents讀取事件讀到了事件就會(huì)調(diào)用processEventsLocked處理事件處理完成后調(diào)用getInputDevicesLocked獲取輸入設(shè)備信息調(diào)用mPolicy->notifyInputDevicesChanged函數(shù)利用InputManagerService的代理通過(guò)Handler發(fā)送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知輸入設(shè)備發(fā)生了變化***調(diào)用mQueuedListener->flush(),將事件隊(duì)列中的所有事件交給
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 黑龍江2025年黑龍江省科學(xué)院智能制造研究所招聘博士科研人員筆試歷年參考題庫(kù)附帶答案詳解
- 職業(yè)健康與員工職業(yè)發(fā)展:醫(yī)療組織健康績(jī)效
- 菏澤2025年山東菏澤巨野縣中醫(yī)醫(yī)院招聘急需專業(yè)技術(shù)人員26人筆試歷年參考題庫(kù)附帶答案詳解
- 秦皇島2025年河北秦皇島市體育局招聘事業(yè)單位工作人員2人筆試歷年參考題庫(kù)附帶答案詳解
- 湛江廣東湛江市坡頭區(qū)財(cái)政局招聘三類編外人員筆試歷年參考題庫(kù)附帶答案詳解
- 海南2025年海南省第二衛(wèi)生學(xué)校招聘20人筆試歷年參考題庫(kù)附帶答案詳解
- 杭州浙江杭州市東潤(rùn)外國(guó)語(yǔ)學(xué)校編外人員招聘4人筆試歷年參考題庫(kù)附帶答案詳解
- 成都2025年四川成都青羊區(qū)招聘社區(qū)工作者和黨建服務(wù)專員117人筆試歷年參考題庫(kù)附帶答案詳解
- 廣州廣東廣州市越秀區(qū)東山街招聘輔助人員筆試歷年參考題庫(kù)附帶答案詳解
- 天津2025年天津市市場(chǎng)監(jiān)督管理委員會(huì)所屬事業(yè)單位招聘13人筆試歷年參考題庫(kù)附帶答案詳解
- 癌癥患者生活質(zhì)量量表EORTC-QLQ-C30
- QCT55-2023汽車座椅舒適性試驗(yàn)方法
- 孕產(chǎn)婦妊娠風(fēng)險(xiǎn)評(píng)估表
- 消化系統(tǒng)疾病健康教育宣教
- 河南省洛陽(yáng)市2023-2024學(xué)年九年級(jí)第一學(xué)期期末質(zhì)量檢測(cè)數(shù)學(xué)試卷(人教版 含答案)
- Unit-3-Reading-and-thinking課文詳解課件-高中英語(yǔ)人教版必修第二冊(cè)
- 新版出口報(bào)關(guān)單模板
- 14K118 空調(diào)通風(fēng)管道的加固
- 加油站財(cái)務(wù)管理制度細(xì)則
- 全過(guò)程工程咨詢服務(wù)技術(shù)方案
- YS/T 1152-2016粗氫氧化鈷
評(píng)論
0/150
提交評(píng)論