亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Android事件處理流程

系統(tǒng) 1848 0

Init------------zygote---------system-server-------------------windosmanager ------------------------------------------------------------ UEventObserver
------------------------------------------------------------ InputDeviceRead
-------------------------------------------------------------InputDispatcher
-------------------------------------------------------------DisplayEventThr
-------------------------------------------------------------ActivityManager

EventHub :
而 事件的傳入是從EventHub開始的,EventHub是事件的抽象結(jié)構(gòu),維護著系統(tǒng)設(shè)備的運行情況,設(shè)備類型包括Keyboard、 TouchScreen、TraceBall。它在系統(tǒng)啟動的時候會通過open_device方法將系統(tǒng)提供的輸入設(shè)備都增加到這個抽象結(jié)構(gòu)中,并維護 一個所有輸入設(shè)備的文件描述符,如果輸入設(shè)備是鍵盤的話還會讀取/system/usr/keylayout/目錄下對應(yīng)鍵盤設(shè)備的映射文件,另外 getEvent方法是對EventHub中的設(shè)備文件描述符使用poll操作等侍驅(qū)動層事件的發(fā)生,如果發(fā)生的事件是鍵盤事件,則調(diào)用Map函數(shù)按照映 射文件轉(zhuǎn)換成相應(yīng)的鍵值并將掃描碼和鍵碼返回給KeyInputQueue。
KeyLayoutMap主要是讀取鍵盤映射文件并將鍵盤掃描碼和鍵碼進行轉(zhuǎn)換

frameworks/base/core/jni/server/ com_android_server_KeyInputQueue.cpp
EventHub和KeyinputQueue的JNI接口層


KeyinputQueue:
在線程InputDeviceReader中會根據(jù)事件的類型以及事件值進行判斷處理,從而確定這個事件對應(yīng)的設(shè)備狀態(tài)是否發(fā)生了改變并相應(yīng)的改變對這個設(shè)備的描述結(jié)構(gòu)InputDevice。
getEvent:在給定時間段時看是否有事件發(fā)生,如果有的話返回true否則false。

Windowmanager:
(frameworks/base/services/java/com/android/server/windowmanagerservice.java)
進 程Windowmanager會創(chuàng)建一個線程(InputDispatcherThread),在這個線程里從事件隊列中讀取發(fā)生的事件 (QueuedEvent ev = mQueue.getEvent()),并根據(jù)讀取到事件類型的不同分成三類(KEYBOARD、TOUCHSCREEN、TRACKBALL),分別進 行處理,例如鍵盤事件會調(diào)用dispatchKey((KeyEvent)ev.event, 0, 0)以將事件通過Binder發(fā)送給具有焦點的窗口應(yīng)用程序,然后調(diào)用 mQueue.recycleEvent(ev)繼續(xù)等侍鍵盤事件的發(fā)生;如果是觸摸屏事件則調(diào)用dispatchPointer(ev, (MotionEvent)ev.event, 0, 0),這里會根據(jù)事件的種類(UP、DOWN、MOVE、OUT_SIDE等)進行判斷并處理,比如Cancel或?qū)⑹录l(fā)送到具有權(quán)限的指定的窗口中 去;

Android 輸入事件流程

EventHub

EventHub對輸入設(shè)備進行了封裝。輸入設(shè)備驅(qū)動程序?qū)τ脩艨臻g應(yīng)用程序提供一些設(shè)備文件,這些設(shè)備文件放在/dev/input里面。

EventHub掃描/dev/input下所有設(shè)備文件,并打開它們。

C代碼
  1. bool EventHub::openPlatformInput( void )
  2. {
  3. ...
  4. mFDCount=1;
  5. mFDs=(pollfd*)calloc(1, sizeof (mFDs[0]));
  6. mDevices=(device_t**)calloc(1, sizeof (mDevices[0]));
  7. mFDs[0].events=POLLIN;
  8. mDevices[0]=NULL;
  9. res=scan_dir(device_path);
  10. ...
  11. return true ;
  12. }



EventHub對外提供了一個函數(shù)用于從輸入設(shè)備文件中讀取數(shù)據(jù)。

C代碼
  1. bool EventHub::getEvent(int32_t*outDeviceId,int32_t*outType,
  2. int32_t*outScancode,int32_t*outKeycode,uint32_t*outFlags,
  3. int32_t*outValue,nsecs_t*outWhen)
  4. {
  5. ...
  6. while (1){
  7. //First,reportanydevicesthathadlastbeenadded/removed.
  8. if (mClosingDevices!=NULL){
  9. device_t*device=mClosingDevices;
  10. LOGV( "Reportingdeviceclosed:id=0x%x,name=%s/n" ,
  11. device->id,device->path.string());
  12. mClosingDevices=device->next;
  13. *outDeviceId=device->id;
  14. if (*outDeviceId==mFirstKeyboardId)*outDeviceId=0;
  15. *outType=DEVICE_REMOVED;
  16. delete device;
  17. return true ;
  18. }
  19. if (mOpeningDevices!=NULL){
  20. device_t*device=mOpeningDevices;
  21. LOGV( "Reportingdeviceopened:id=0x%x,name=%s/n" ,
  22. device->id,device->path.string());
  23. mOpeningDevices=device->next;
  24. *outDeviceId=device->id;
  25. if (*outDeviceId==mFirstKeyboardId)*outDeviceId=0;
  26. *outType=DEVICE_ADDED;
  27. return true ;
  28. }
  29. release_wake_lock(WAKE_LOCK_ID);
  30. pollres=poll(mFDs,mFDCount,-1);
  31. acquire_wake_lock(PARTIAL_WAKE_LOCK,WAKE_LOCK_ID);
  32. if (pollres<=0){
  33. if (errno!=EINTR){
  34. LOGW( "selectfailed(errno=%d)/n" ,errno);
  35. usleep(100000);
  36. }
  37. continue ;
  38. }
  39. for (i=1;i<mFDCount;i++){
  40. if (mFDs[i].revents){
  41. LOGV( "reventsfor%d=0x%08x" ,i,mFDs[i].revents);
  42. if (mFDs[i].revents&POLLIN){
  43. res=read(mFDs[i].fd,&iev, sizeof (iev));
  44. if (res== sizeof (iev)){
  45. LOGV( "%sgot:t0=%d,t1=%d,type=%d,code=%d,v=%d" ,
  46. mDevices[i]->path.string(),
  47. ( int )iev.time.tv_sec,( int )iev.time.tv_usec,
  48. iev.type,iev.code,iev.value);
  49. *outDeviceId=mDevices[i]->id;
  50. if (*outDeviceId==mFirstKeyboardId)*outDeviceId=0;
  51. *outType=iev.type;
  52. *outScancode=iev.code;
  53. if (iev.type==EV_KEY){
  54. err=mDevices[i]->layoutMap->map(iev.code,outKeycode,outFlags);
  55. LOGV( "iev.code=%doutKeycode=%doutFlags=0x%08xerr=%d/n" ,
  56. iev.code,*outKeycode,*outFlags,err);
  57. if (err!=0){
  58. *outKeycode=0;
  59. *outFlags=0;
  60. }
  61. } else {
  62. *outKeycode=iev.code;
  63. }
  64. *outValue=iev.value;
  65. *outWhen=s2ns(iev.time.tv_sec)+us2ns(iev.time.tv_usec);
  66. return true ;
  67. } else {
  68. if (res<0){
  69. LOGW( "couldnotgetevent(errno=%d)" ,errno);
  70. } else {
  71. LOGE( "couldnotgetevent(wrongsize:%d)" ,res);
  72. }
  73. continue ;
  74. }
  75. }
  76. }
  77. }
  78. ...
  79. }




對于按鍵事件,調(diào)用mDevices[i]->layoutMap->map進行映射。映射實際是由 KeyLayoutMap::map完成的,KeyLayoutMap類里讀取配置文件qwerty.kl,由配置 文件 qwerty.kl 決定鍵值的映射關(guān)系。你可以通過修 改./development/emulator/keymaps/qwerty.kl來改變鍵值的映射關(guān)系。
JNI 函數(shù)

在 frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文 件中,向 JAVA提供了函數(shù)android_server_KeyInputQueue_readEvent,用于讀 取輸入設(shè)備事件。

C代碼
  1. static jboolean
  2. android_server_KeyInputQueue_readEvent(JNIEnv*env,jobjectclazz,
  3. jobjectevent)
  4. {
  5. gLock.lock();
  6. sphub=gHub;
  7. if (hub==NULL){
  8. hub= new EventHub;
  9. gHub=hub;
  10. }
  11. gLock.unlock();
  12. int32_tdeviceId;
  13. int32_ttype;
  14. int32_tscancode,keycode;
  15. uint32_tflags;
  16. int32_tvalue;
  17. nsecs_twhen;
  18. bool res=hub->getEvent(&deviceId,&type,&scancode,&keycode,
  19. &flags,&value,&when);
  20. env->SetIntField(event,gInputOffsets.mDeviceId,(jint)deviceId);
  21. env->SetIntField(event,gInputOffsets.mType,(jint)type);
  22. env->SetIntField(event,gInputOffsets.mScancode,(jint)scancode);
  23. env->SetIntField(event,gInputOffsets.mKeycode,(jint)keycode);
  24. env->SetIntField(event,gInputOffsets.mFlags,(jint)flags);
  25. env->SetIntField(event,gInputOffsets.mValue,value);
  26. env->SetLongField(event,gInputOffsets.mWhen,
  27. (jlong)(nanoseconds_to_milliseconds(when)));
  28. return res;
  29. }



readEvent調(diào)用hub->getEvent讀了取事件,然后轉(zhuǎn)換成JAVA的結(jié)構(gòu)。
事件中轉(zhuǎn)線程

在frameworks/base/services/java/com/android/server/KeyInputQueue.java 里創(chuàng)建了一個線程,它循環(huán)的讀取事件,然后把事件放入事件隊列里。

Java代碼
  1. ThreadmThread= new Thread( "InputDeviceReader" ){
  2. public void run(){
  3. android.os.Process.setThreadPriority(
  4. android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
  5. try {
  6. RawInputEventev= new RawInputEvent();
  7. while ( true ){
  8. InputDevicedi;
  9. readEvent(ev);
  10. send=preprocessEvent(di,ev);
  11. addLocked(di,curTime,ev.flags,...,me);
  12. }
  13. }
  14. };



輸入事件分發(fā)線程

在frameworks/base/services/java/com/android/server/WindowManagerService.java里創(chuàng)建了一個輸入事件分發(fā)線程,它負責把事件分發(fā)到相應(yīng)的窗口上去。

Java代碼
  1. mQueue.getEvent
  2. dispatchKey/dispatchPointer/dispatchTrackball





按鍵,觸摸屏流程分析

按鍵觸摸屏流程分析:
WindowManagerService類的構(gòu)造函數(shù)
WindowManagerService()
mQueue = new KeyQ();
因為 WindowManagerService.java (frameworks/base/services/java/com/android/server)中有:
private class KeyQ extends KeyInputQueue
KeyQ 是抽象類 KeyInputQueue 的實現(xiàn),所以 new KeyQ類的時候?qū)嶋H上在 KeyInputQueue 類中創(chuàng)建了
一個線程 InputDeviceReader 專門用來沖設(shè)備讀取按鍵事件,代碼:
Thread mThread = new Thread("InputDeviceReader") {
public void run()
{
在循環(huán)中調(diào)用:readEvent(ev);
...
send = preprocessEvent(di, ev);
實際調(diào)用的是 KeyQ 類的 preprocessEvent 函數(shù)
...
int keycode = rotateKeyCodeLocked(ev.keycode);
int[] map = mKeyRotationMap;
for (int i=0; i<N; i+=2)
{
if (map[i] == keyCode)
return map[i+1];
} //
addLocked(di, curTime, ev.flags,RawInputEvent.CLASS_KEYBOARD,newKeyEvent(di, di.mDownTime, curTime, down,keycode, 0, scancode,...));
QueuedEvent ev = obtainLocked(device, when, flags, classType, event);
}
}

readEvent() 實際上調(diào)用的是 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);
調(diào)用的是 EventHub.cpp (frameworks/base/libs/ui)中的:
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
在函數(shù)中調(diào)用了讀設(shè)備操作:res = read(mFDs[i].fd, &iev, sizeof(iev));


在構(gòu)造函數(shù) WindowManagerService()調(diào)用 new KeyQ() 以后接著調(diào)用了:
mInputThread = new InputDispatcherThread();
...
mInputThread.start();
來啟動一個線程 InputDispatcherThread
run()
process();
QueuedEvent ev = mQueue.getEvent(...)
因為WindowManagerService類中: final KeyQ mQueue;
所以實際上 InputDispatcherThread 線程實際上從 KeyQ 的事件隊列中讀取按鍵事件。
switch (ev.classType)
case RawInputEvent.CLASS_KEYBOARD:
...
dispatchKey((KeyEvent)ev.event, 0, 0);
mQueue.recycleEvent(ev);
break;
case RawInputEvent.CLASS_TOUCHSCREEN:
//Log.i(TAG, "Read next event " + ev);
dispatchPointer(ev, (MotionEvent)ev.event, 0, 0);
break;

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


KeyInputQueue.java (frameworks/base/services/java/com/android/server):
的線程 Thread mThread = new Thread("InputDeviceReader") 本地調(diào)用:
readEvent(ev);讀取按鍵。readEvent 調(diào)用的是文件:
com_android_server_KeyInputQueue.cpp (frameworks/base/services/jni)中的函數(shù):
static jboolean android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
jobject event)
android_server_KeyInputQueue_readEvent中有:
hub = new EventHub;
bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
&flags, &value, &when);

hub->getEvent 調(diào)用的是
EventHub.cpp (frameworks/base/libs/ui) 文件中的函數(shù):
bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType,
int32_t* outScancode, int32_t* outKeycode, uint32_t *outFlags,
int32_t* outValue, nsecs_t* outWhen)
讀取按鍵。

class RefBase::weakref_impl : public RefBase::weakref_type



在系統(tǒng)啟動后,android 會通過
static const char *device_path = "/dev/input";
bool EventHub::openPlatformInput(void)
res = scan_dir(device_path);


通過下面的函數(shù)打開設(shè)備。
int EventHub::open_device(const char *deviceName)
{
...
fd = open(deviceName, O_RDWR);
...
mFDs[mFDCount].fd = fd;
mFDs[mFDCount].events = POLLIN;
...
ioctl(mFDs[mFDCount].fd, EVIOCGNAME(sizeof(devname)-1), devname);
...
const char* root = getenv("ANDROID_ROOT");
snprintf(keylayoutFilename, sizeof(keylayoutFilename),
"%s/usr/keylayout/%s.kl", root, tmpfn);
...
device->layoutMap->load(keylayoutFilename);
...
}
打開設(shè)備的時候,如果 device->classes&CLASS_KEYBOARD 不等于 0 表明是鍵盤。
常用輸入設(shè)備的定義有:
enum {
CLASS_KEYBOARD = 0x00000001, //鍵盤
CLASS_ALPHAKEY = 0x00000002, //
CLASS_TOUCHSCREEN = 0x00000004, //觸摸屏
CLASS_TRACKBALL = 0x00000008 //軌跡球
};
打開鍵盤設(shè)備的時候通過上面的 ioctl 獲得設(shè)備名稱,命令字 EVIOCGNAME 的定義在文件:
kernel/include/linux/input.h 中。
#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
在內(nèi)核鍵盤驅(qū)動文件 drivers/input/keyboard/pxa27x_keypad.c 中定義了設(shè)備名稱:pxa27x-keypad
static struct platform_driver pxa27x_keypad_driver = {
.probe = pxa27x_keypad_probe,
.remove = __devexit_p(pxa27x_keypad_remove),
.suspend = pxa27x_keypad_suspend,
.resume = pxa27x_keypad_resume,
.driver = {
.name = "pxa27x-keypad",
.owner = THIS_MODULE,
},
};
ANDROID_ROOT 為環(huán)境變量,在android的命令模式下通過 printenv 可以知道它為: system
所以 keylayoutFilename 為:/system/usr/keylayout/pxa27x-keypad.kl
pxa27x-keypad.kl 定義了按鍵映射,具體內(nèi)容如下:
----------------------
# NUMERIC KEYS 3x4
key 2 1
key 3 2
key 4 3
key 5 4
key 6 5
key 7 6
key 8 7
key 9 8
key 10 9
key 11 0
key 83 POUND
key 55 STAR

# FUNCTIONAL KEYS
key 231 MENU WAKE_DROPPED
key 192 BACK WAKE_DROPPED
key 193 HOME WAKE
key 107 DEL WAKE
key 102 CALL WAKE_DROPPED
key 158 ENDCALL WAKE_DROPPED
key 28 DPAD_CENTER WAKE
key 115 VOLUME_UP
key 114 VOLUME_DOWN
----------------------
如果沒有定義鍵盤映射文件,那么默認使用系統(tǒng)的 /system/usr/keylayout/qwerty.kl
可以修改 /system/usr/keylayout/qwerty.kl 文件改變Android公司的按鍵映射。

device->layoutMap->load(keylayoutFilename) 調(diào)用的是文件:
KeyLayoutMap.cpp (frameworks/base/libs/ui)中的函數(shù):
status_t KeyLayoutMap::load(const char* filename)通過解析 pxa27x-keypad.kl
把按鍵的映射關(guān)系保存在 :KeyedVector<int32_t,Key> m_keys; 中。
當獲得按鍵事件以后調(diào)用:
status_t KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags)
由映射關(guān)系 KeyedVector<int32_t,Key> m_keys 把掃描碼轉(zhuǎn)換成andorid上層可以識別的按鍵。

Android事件處理流程


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。。?/p>

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产福利在线视频 | 久久久精品国产免费观看同学 | 久久综合干 | 免费国产成人 | 真实国语对白视频播放 | 国产成人一区二区三区 | 欧美夜色 | 久久久久久久亚洲精品一区 | 久草在线视频精品 | 狼狼色丁香久久婷婷综合五月 | 毛片毛片毛片毛片出来毛片 | 操操网站 | 天天色天天操天天射 | 久热伊人| 亚洲成人在线免费视频 | 国产高清美女一级a毛片久久 | 久久综合给合久久狠狠狠97色 | 国产精品毛片一区二区三区 | 国产成人一区二区三区精品久久 | 国产高清精品一级毛片 | 免费观看黄色小视频 | 欧美在线成人免费国产 | 日本a中文字幕 | 久久国产精品-国产精品 | 欧美日韩操 | 奇米影视7777久久精品 | 久草在线首页 | 第一福利在线 | 激情婷婷综合 | 久草在线免费看视频 | 可以免费观看的一级毛片 | 午夜男人 | 奇米777888| 怡红院成人永久免费看 | 久久视频在线看 | 中文字幕日韩哦哦哦 | 国产精品久久久久不卡绿巨人 | 亚洲精品国产一区二区在线 | 色综合精品久久久久久久 | 国产成人精品本亚洲 | 日日摸夜夜爽日日放碰 |