android事件驱动.docx
- 文档编号:10428267
- 上传时间:2023-02-11
- 格式:DOCX
- 页数:14
- 大小:21KB
android事件驱动.docx
《android事件驱动.docx》由会员分享,可在线阅读,更多相关《android事件驱动.docx(14页珍藏版)》请在冰豆网上搜索。
android事件驱动
Init-----------zygote---------system-server-------------------windosmanager ------------------------------------------------------------UEventObserver
------------------------------------------------------------InputDeviceRead
-------------------------------------------------------------InputDispatcher
-------------------------------------------------------------DisplayEventThr
-------------------------------------------------------------ActivityManager
EventHub:
而事件的传入是从EventHub开始的,EventHub是事件的抽象结构,维护着系统设备的运行情况,设备类型包括Keyboard、TouchScreen、TraceBall。
它在系统启动的时候会通过open_device方法将系统提供的输入设备都增加到这个抽象结构中,并维护一个所有输入设备的文件描述符,如果输入设备是键盘的话还会读取/system/usr/keylayout/目录下对应键盘设备的映射文件,另外getEvent方法是对EventHub中的设备文件描述符使用poll操作等侍驱动层事件的发生,如果发生的事件是键盘事件,则调用Map函数按照映射文件转换成相应的键值并将扫描码和键码返回给KeyInputQueue。
KeyLayoutMap主要是读取键盘映射文件并将键盘扫描码和键码进行转换
frameworks\base\core\jni\server\com_android_server_KeyInputQueue.cpp
EventHub和KeyinputQueue的JNI接口层
KeyinputQueue:
在线程InputDeviceReader中会根据事件的类型以及事件值进行判断处理,从而确定这个事件对应的设备状态是否发生了改变并相应的改变对这个设备的描述结构InputDevice。
getEvent:
在给定时间段时看是否有事件发生,如果有的话返回true否则false。
Windowmanager:
(frameworks/base/services/java/com/android/server/windowmanagerservice.java)
进程Windowmanager会创建一个线程(InputDispatcherThread),在这个线程里从事件队列中读取发生的事件(QueuedEventev=mQueue.getEvent()),并根据读取到事件类型的不同分成三类(KEYBOARD、TOUCHSCREEN、TRACKBALL),分别进行处理,例如键盘事件会调用dispatchKey((KeyEvent)ev.event,0,0)以将事件通过Binder发送给具有焦点的窗口应用程序,然后调用mQueue.recycleEvent(ev)继续等侍键盘事件的发生;如果是触摸屏事件则调用dispatchPointer(ev,(MotionEvent)ev.event,0,0),这里会根据事件的种类(UP、DOWN、MOVE、OUT_SIDE等)进行判断并处理,比如Cancel或将事件发送到具有权限的指定的窗口中去;
Android输入事件流程
EventHub
EventHub对输入设备进行了封装。
输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。
EventHub扫描/dev/input下所有设备文件,并打开它们。
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.
10. res = scan_dir(device_path);
11....
12. return true;
13.}
EventHub对外提供了一个函数用于从输入设备文件中读取数据。
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.
8. // First, report any devices that had last been added/removed.
9. if (mClosingDevices !
= NULL) {
10. device_t* device = mClosingDevices;
11. LOGV("Reporting device closed:
id=0x%x, name=%s\n",
12. device->id, device->path.string());
13. mClosingDevices = device->next;
14. *outDeviceId = device->id;
15. if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
16. *outType = DEVICE_REMOVED;
17. delete device;
18. return true;
19. }
20. if (mOpeningDevices !
= NULL) {
21. device_t* device = mOpeningDevices;
22. LOGV("Reporting device opened:
id=0x%x, name=%s\n",
23. device->id, device->path.string());
24. mOpeningDevices = device->next;
25. *outDeviceId = device->id;
26. if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
27. *outType = DEVICE_ADDED;
28. return true;
29. }
30.
31. release_wake_lock(WAKE_LOCK_ID);
32.
33. pollres = poll(mFDs, mFDCount, -1);
34.
35. acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
36.
37. if (pollres <= 0) {
38. if (errno !
= EINTR) {
39. LOGW("select failed (errno=%d)\n", errno);
40. usleep(100000);
41. }
42. continue;
43. }
44.
45. for(i = 1; i < mFDCount; i++) {
46. if(mFDs[i].revents) {
47. LOGV("revents for %d = 0x%08x", i, mFDs[i].revents);
48. if(mFDs[i].revents & POLLIN) {
49. res = read(mFDs[i].fd, &iev, sizeof(iev));
50. if (res == sizeof(iev)) {
51. LOGV("%s got:
t0=%d, t1=%d, type=%d, code=%d, v=%d",
52. mDevices[i]->path.string(),
53. (int) iev.time.tv_sec, (int) iev.time.tv_usec,
54. iev.type, iev.code, iev.value);
55. *outDeviceId = mDevices[i]->id;
56. if (*outDeviceId == mFirstKeyboardId) *outDeviceId = 0;
57. *outType = iev.type;
58. *outScancode = iev.code;
59. if (iev.type == EV_KEY) {
60. err = mDevices[i]->layoutMap->map(iev.code, outKeycode, outFlags);
61. LOGV("iev.code=%d outKeycode=%d outFlags=0x%08x err=%d\n",
62. iev.code, *outKeycode, *outFlags, err);
63. if (err !
= 0) {
64. *outKeycode = 0;
65. *outFlags = 0;
66. }
67. } else {
68. *outKeycode = iev.code;
69. }
70. *outValue = iev.value;
71. *outWhen = s2ns(iev.time.tv_sec) + us2ns(iev.time.tv_usec);
72. return true;
73. } else {
74. if (res<0) {
75. LOGW("could not get event (errno=%d)", errno);
76. } else {
77. LOGE("could not get event (wrong size:
%d)", res);
78. }
79. continue;
80. }
81. }
82. }
83. }
84. ...
85.}
对于按键事件,调用mDevices[i]->layoutMap->map进行映射。
映射实际是由KeyLayoutMap:
:
map完成的,KeyLayoutMap类里读取配置文件qwerty.kl,由配置文件qwerty.kl决定键值的映射关系。
你可以通过修改./development/emulator/keymaps/qwerty.kl来改变键值的映射关系。
JNI函数
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件中,向JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读取输入设备事件。
C代码
1.static jboolean
2.android_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz,
3. jobject event)
4.{
5. gLock.lock();
6. sp hub = gHub;
7. if (hub == NULL) {
8. hub = new EventHub;
9. gHub = hub;
10. }
11. gLock.unlock();
12.
13. int32_t deviceId;
14. int32_t type;
15. int32_t scancode, keycode;
16. uint32_t flags;
17. int32_t value;
18. nsecs_t when;
19. bool res = hub->getEvent(&deviceId, &type, &scancode, &keycode,
20. &flags, &value, &when);
21.
22. env->SetIntField(event, gInputOffsets.mDeviceId, (jint)deviceId);
23. env->SetIntField(event, gInputOffsets.mType, (jint)type);
24. env->SetIntField(event, gInputOffsets.mScancode, (jint)scancode);
25. env->SetIntField(event, gInputOffsets.mKeycode, (jint)keycode);
26. env->SetIntField(event, gInputOffsets.mFlags, (jint)flags);
27. env->SetIntField(event, gInputOffsets.mValue, value);
28. env->SetLongField(event, gInputOffsets.mWhen,
29. (jlong)(nanoseconds_to_milliseconds(when)));
30.
31. return res;
32.}
readEvent调用hub->getEvent读了取事件,然后转换成JAVA的结构。
事件中转线程
在frameworks/base/services/java/com/android/server/KeyInputQueue.java里创建了一个线程,它循环的读取事件,然后把事件放入事件队列里。
Java代码
1.Thread mThread = new Thread("InputDeviceReader") {
2. public void run() {
3. android.os.Process.setThreadPriority(
4. android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY);
5.
6. try {
7. RawInputEvent ev = new RawInputEvent();
8. while (true) {
9. InputDevice di;
10.
11. readEvent(ev);
12.
13. send = preprocessEvent(di, ev);
14. addLocked(di, curTime, ev.flags, ..., me);
15. }
16. }
17. };
输入事件分发线程
在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
Java代码
1.mQueue.getEvent
2.dispatchKey/dispatchPointer/dispatchTrackball
按键,触摸屏流程分析
按键触摸屏流程分析:
WindowManagerService类的构造函数
WindowManagerService()
mQueue=newKeyQ();
因为WindowManagerService.java(frameworks\base\services\java\com\android\server)中有:
privateclassKeyQextendsKeyInputQueue
KeyQ是抽象类 KeyInputQueue的实现,所以newKeyQ类的时候实际上在KeyInputQueue类中创建了
一个线程 InputDeviceReader专门用来从设备读取按键事件,代码:
ThreadmThread=newThread("InputDeviceReader"){
publicvoidrun()
{
在循环中调用:
readEvent(ev);
...
send=preprocessEvent(di,ev);
实际调用的是KeyQ类的preprocessEvent函数
...
intkeycode=rotateKeyCodeLocked(ev.keycode);
int[]map=mKeyRotationMap;
for(inti=0;i { if(map[i]==keyCode) returnmap[i+1]; }// addLocked(di,curTime,ev.flags,RawInputEvent.CLASS_KEYBOARD,newKeyEvent(di,di.mDownTime,curTime,down,keycode,0,scancode,...)); QueuedEventev=obtainLocked(device,when,flags,classType,event); } } readEvent()实际上调用的是com_android_server_KeyInputQueue.cpp(frameworks\base\services\jni)中的: staticjbooleanandroid_server_KeyInputQueue_readEv
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- android 事件 驱动
![提示](https://static.bdocx.com/images/bang_tan.gif)