Android編程輸入事件流程詳解
本文實(shí)例講述了Android編程輸入事件流程。分享給大家供大家參考,具體如下:
EventHub對(duì)輸入設(shè)備進(jìn)行了封裝。輸入設(shè)備驅(qū)動(dòng)程序?qū)τ脩艨臻g應(yīng)用程序提供一些設(shè)備文件,這些設(shè)備文件放在/dev/input里面。
EventHub掃描/dev/input下所有設(shè)備文件,并打開(kāi)它們。
bool EventHub::openPlatformInput(void)
{
...
mFDCount = 1;
mFDs = (pollfd *)calloc(1, sizeof(mFDs[0]));
mDevices = (device_t **)calloc(1, sizeof(mDevices[0]));
mFDs[0].events = POLLIN;
mDevices[0] = NULL;
res = scan_dir(device_path);
...
return true;
}
EventHub對(duì)外提供了一個(gè)函數(shù)用于從輸入設(shè)備文件中讀取數(shù)據(jù)。
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
{
...
while(1) {
// First, report any devices that had last been added/removed.
if (mClosingDevices != NULL) {
device_t* device = mClosingDevices;
LOGV("Reporting device closed: id=0x%x, name=%s\n",
device->id, device->path.string());
mClosingDevices = device->next;
*outDeviceId = device->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = DEVICE_REMOVED;
delete device;
return true;
}
if (mOpeningDevices != NULL) {
device_t* device = mOpeningDevices;
LOGV("Reporting device opened: id=0x%x, name=%s\n",
device->id, device->path.string());
mOpeningDevices = device->next;
*outDeviceId = device->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = DEVICE_ADDED;
return true;
}
release_wake_lock(WAKE_LOCK_ID);
pollres = poll(mFDs, mFDCount, -1);
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
if (pollres <= 0) {
if (errno != EINTR) {
LOGW("select failed (errno=%d)\n", errno);
usleep(100000);
}
continue;
}
for(i = 1; i < mFDCount; i++) {
if(mFDs[i].revents) {
LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
if(mFDs[i].revents & POLLIN) {
res = read(mFDs[i].fd, &iev, sizeof(iev));
if (res == sizeof(iev)) {
LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d",
mDevices[i]->path.string(),
(int) iev.time.tv_sec, (int) iev.time.tv_usec,
iev.type, iev.code, iev.value);
*outDeviceId = mDevices[i]->id;
if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
*outType = iev.type;
*outScancode = iev.code;
if (iev.type == EV_KEY) {
err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
iev.code, *outKeycode, *outFlags, err);
if (err != 0) {
*outKeycode = 0;
*outFlags = 0;
}
} else {
*outKeycode = iev.code;
}
*outValue = iev.value;
*outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
return true;
} else {
if (res<0) {
LOGW("could not get event (errno=%d)", errno);
} else {
LOGE("could not get event (wrong size: %d)", res);
}
continue;
}
}
}
}
...
}
對(duì)于按鍵事件,調(diào)用mDevices[i]->layoutMap->map進(jìn)行映射。映射實(shí)際是由 KeyLayoutMap::map完成的,KeyLayoutMap類里讀取配置文件qwerty.kl,由配置文件qwerty.kl決定鍵值的映射關(guān)系。你可以通過(guò)修改./development/emulator/keymaps/qwerty.kl來(lái)改變鍵值的映射關(guān)系。
JNI函數(shù)
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件中,向JAVA提供了函數(shù)android_server_KeyInputQueue_readEvent,用于讀取輸入設(shè)備事件。
static jboolean
android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event)
{
gLock.lock();
sp hub = gHub;
if (hub == NULL) {
hub = new EventHub;
gHub = hub;
}
gLock.unlock();
int32_t deviceId;
int32_t type;
int32_t scancode, keycode;
uint32_t flags;
int32_t value;
nsecs_t when;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode, &flags, &value, &when);
env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
env->SetIntField(event, gInputOffsets.mType, (jint)type);
env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
env->SetIntField(event, gInputOffsets.mValue, value);
env->SetLongField(event, gInputOffsets.mWhen, (jlong)(nanoseconds_to_milliseconds(when)));
return res;
}
readEvent調(diào)用hub->getEvent讀了取事件,然后轉(zhuǎn)換成JAVA的結(jié)構(gòu)。
事件中轉(zhuǎn)線程
在frameworks/base/services/java/com/android/server/KeyInputQueue.java里創(chuàng)建了一個(gè)線程,它循環(huán)的讀取事件,然后把事件放入事件隊(duì)列里。
Thread mThread = new Thread("InputDeviceReader") {
public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
try {
RawInputEvent ev = new RawInputEvent();
while (true) {
InputDevice di;
readEvent(ev);
send = preprocessEvent(di, ev);
addLocked(di, curTime, ev.flags, ..., me);
}
}
};
}
輸入事件分發(fā)線程
在frameworks/base/services/java/com/android/server/WindowManagerService.java里創(chuàng)建了一個(gè)輸入事件分發(fā)線程,它負(fù)責(zé)把事件分發(fā)到相應(yīng)的窗口上去。
mQueue.getEvent dispatchKey/dispatchPointer/dispatchTrackball
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android開(kāi)發(fā)入門與進(jìn)階教程》、《Android調(diào)試技巧與常見(jiàn)問(wèn)題解決方法匯總》、《Android多媒體操作技巧匯總(音頻,視頻,錄音等)》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
- Android APP啟動(dòng)方式、啟動(dòng)流程及啟動(dòng)優(yōu)化分析
- 分析Android中應(yīng)用的啟動(dòng)流程
- Android教程之開(kāi)機(jī)流程全面解析
- 從源碼分析Android的Glide庫(kù)的圖片加載流程及特點(diǎn)
- Android系統(tǒng)關(guān)機(jī)的全流程解析
- Android Bluetooth藍(lán)牙技術(shù)使用流程詳解
- Android Mms之:短信發(fā)送流程(圖文詳解)
- Android Mms之:接收信息流程(圖文詳解)
- Android中打電話的數(shù)據(jù)流程分析
- Android 2.3 撥號(hào)上網(wǎng)流程從源碼角度進(jìn)行分析
相關(guān)文章
Android基于ListView實(shí)現(xiàn)類似QQ空間的滾動(dòng)翻頁(yè)與滾動(dòng)加載效果
這篇文章主要介紹了Android基于ListView實(shí)現(xiàn)類似QQ空間的滾動(dòng)翻頁(yè)與滾動(dòng)加載效果,涉及ListView相關(guān)屬性與方法的操作技巧,需要的朋友可以參考下2016-08-08
android 無(wú)須root截圖方案的實(shí)現(xiàn)
這篇文章主要介紹了android 無(wú)須root截圖方案的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03
Android EditText實(shí)現(xiàn)輸入金額類型詳解
EditText是Android中一個(gè)非常實(shí)用的控件,有很多InputType,可以來(lái)達(dá)到不同的輸入效果,下面這篇文章主要給大家介紹了關(guān)于Android EditText實(shí)現(xiàn)輸入金額類型的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-09-09
Android網(wǎng)絡(luò)監(jiān)聽(tīng)和網(wǎng)絡(luò)判斷示例介紹
大家好,本篇文章主要講的是Android網(wǎng)絡(luò)監(jiān)聽(tīng)和網(wǎng)絡(luò)判斷示例介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2021-12-12
Android設(shè)計(jì)模式之Builder模式詳解
這篇文章主要為大家詳細(xì)介紹了Android設(shè)計(jì)模式之Builder模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
android 調(diào)用JNI SO動(dòng)態(tài)庫(kù)的方法
android 調(diào)用JNI 分為靜態(tài)調(diào)用與動(dòng)態(tài)調(diào)用,接下來(lái)通過(guò)本文給大家介紹android 調(diào)用JNI SO動(dòng)態(tài)庫(kù)的方法,感興趣的朋友一起看看吧2021-11-11
Android?App設(shè)計(jì)規(guī)范深入講解
隨著安卓智能手機(jī)不停的更新?lián)Q代,安卓手機(jī)系統(tǒng)越來(lái)越完美,屏幕尺寸也越來(lái)越大啦,下面這篇文章主要給大家介紹了關(guān)于Android?App設(shè)計(jì)規(guī)范的相關(guān)資料,需要的朋友可以參考下2022-10-10
Android編程實(shí)現(xiàn)多列顯示的下拉列表框Spinner功能示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)多列顯示的下拉列表框Spinner功能,結(jié)合具體實(shí)例形式分析了Android多列表顯示功能的相關(guān)布局操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06
Android小程序?qū)崿F(xiàn)音樂(lè)播放列表
這篇文章主要為大家詳細(xì)介紹了Android小程序?qū)崿F(xiàn)音樂(lè)播放列表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05
基于Android week view仿小米和iphone日歷效果
這篇文章主要為大家詳細(xì)介紹了基于Android week view仿小米和iphone日歷效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11

