【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android中怎么利用 Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng)_第1頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android中怎么利用 Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng)_第2頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android中怎么利用 Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng)_第3頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android中怎么利用 Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng)_第4頁(yè)
【移動(dòng)應(yīng)用開(kāi)發(fā)技術(shù)】Android中怎么利用 Input子系統(tǒng)監(jiān)聽(tīng)線程的啟動(dòng)_第5頁(yè)
免費(fèi)預(yù)覽已結(jié)束,剩余1頁(yè)可下載查看

付費(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論