[轉]Android事件處理分析

原文地址 http://www.cnblogs.com/cnhome/archive/2009/12/22/1629435.html

Android事件處理分析

>>>>>>>>>>> 文章發表較早,某些地方可能和現在Android2.1對應不上,自行處理 :) <<<<<<<<<<<<<<<<<<<<<<,

 

Posted on 2009-12-22 01:21 Minisky

 

按鍵事件

對於按鍵事件,調用mDevices->layoutMap->map進行映射。映射實際是由 KeyLayoutMap::map完成的,KeyLayoutMap類裏讀取配置文件qwerty.kl,由配置 文件 qwerty.kl 決定鍵值的映射關係。你可以通過修 改system/usr/keylayout/qwerty.kl來改變鍵值的映射關係。

JNI 函數
在 frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文 件中,向 JAVA提供了函數android_server_KeyInputQueue_readEvent,用於讀 取輸入設備事件。
C代碼:

 

 readEvent調用hub->getEvent讀了取事件,然後轉換成JAVA的結構。

事件中轉線程
在frameworks/base/services/java/com/android/server/KeyInputQueue.java 裏創建了一個線程,它循環的讀取事件,然後把事件放入事件隊列裏。
Java代碼:

按鍵、觸摸屏流、軌跡球程分析

輸入事件分發線程
在frameworks/base/services/java/com/android/server/WindowManagerService.java裏創建了一個輸入事件分發線程,它負責把事件分發到相應的窗口上去。


按鍵觸摸屏流程分析:

    WindowManagerService類的構造函數

    WindowManagerService()

    mQueue = new KeyQ();

因爲 WindowManagerService.java (frameworks/base/services/java/com/android/server)中有:

private class KeyQ extends KeyInputQueue implements KeyInputQueue.FilterCallback

KeyQ 是抽象類 KeyInputQueue 的實現,所以 new KeyQ類的時候實際上在 KeyInputQueue 類中創建了一個線程 InputDeviceReader 專門用來從設備讀取按鍵事件,

代碼:


readEvent() 實際上調用的是 com_android_server_KeyInputQueue.cpp (frameworks/base/services/jni)中的

static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,jobject event) 來讀取事件,

 

bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,&flags, &value, &when)調用的是EventHub.cpp (frameworks/base/libs/ui)中的:

在函數中調用了讀設備操作:res = read(mFDs.fd, &iev, sizeof(iev));

在構造函數 WindowManagerService()調用 new KeyQ() 以後接着調用了:

來啓動一個線程 InputDispatcherThread

因爲WindowManagerService類中: final KeyQ mQueue;

所以實際上 InputDispatcherThread 線程實際上從 KeyQ 的事件隊列中讀取按鍵事件,在process() 方法中進行處理事件。

===============================================================

補充一些內容:

在寫程序時,需要捕獲KEYCODE_HOME、KEYCODE_ENDCALL、KEYCODE_POWER這幾個按鍵,但是這幾個按鍵系統做了特殊處理,

在進行dispatch之前做了一些操作,HOME除了Keygaurd之外,不分發給任何其他APP,ENDCALL和POWER也類似,所以需要我們系統

處理之前進行處理。

我的做法是自己定義一個FLAG,在自己的程序中添加此FLAG,然後在WindowManagerServices.java中獲取當前窗口的FLAG屬性,如果是我

們自己設置的那個FLAG,則不進行特殊處理,直接分發按鍵消息到我們的APP當中,由APP自己處理。

這部分代碼最好添加在

@Override
boolean preprocessEvent(InputDevice device, RawInputEvent event)

方法中,這個方法是KeyInputQueue中的一個虛函數,在處理按鍵事件之前的一個“預處理”。

PS:對HOME鍵的處理好像必需要修改PhoneWindowManager.java中的interceptKeyTi方法,具體可以參考對KeyGuard程序的處理。

 

===============================================================

 

系統底層事件處理過程


在系統啓動後,android 會通過

通過下面的函數打開設備。

打開設備的時候,如果 device->classes&CLASS_KEYBOARD 不等於 0 表明是鍵盤。

常用輸入設備的定義有:

打開鍵盤設備的時候通過上面的 ioctl 獲得設備名稱,命令字 EVIOCGNAME 的定義在文件:

kernel/include/linux/input.h 中。

#define EVIOCGNAME(len)   _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */

在內核鍵盤驅動文件 drivers/input/keyboard/pxa27x_keypad.c 中定義了設備名稱:pxa27x-keypad

ANDROID_ROOT 爲環境變量,在android的命令模式下通過 printenv 可以知道它爲: system

所以 keylayoutFilename 爲:/system/usr/keylayout/pxa27x-keypad.kl

pxa27x-keypad.kl 定義了按鍵映射,具體內容如下:

如果沒有定義鍵盤映射文件,那麼默認使用系統的 /system/usr/keylayout/qwerty.kl 可以修改 /system/usr/keylayout/qwerty.kl 文件改變Android公司的按鍵映射。

device->layoutMap->load(keylayoutFilename) 調用的是文件 KeyLayoutMap.cpp (frameworks/base/libs/ui)中的函數:

    status_t KeyLayoutMap::load(const char* filename)通過解析 pxa27x-keypad.kl
把按鍵的映射關係保存在 :KeyedVector<int32_t,Key> m_keys; 中。

當獲得按鍵事件以後調用:
status_t KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags)

由映射關係 KeyedVector<int32_t,Key> m_keys 把掃描碼轉換成andorid上層可以識別的按鍵。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章