Android编程输入事件流程详解
本文实例讲述了android编程输入事件流程。分享给大家供大家参考,具体如下:
eventhub对输入设备进行了封装。输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。
eventhub扫描/dev/input下所有设备文件,并打开它们。
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对外提供了一个函数用于从输入设备文件中读取数据。
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; } } } } ... }
对于按键事件,调用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,用于读取输入设备事件。
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调用hub->getevent读了取事件,然后转换成java的结构。
事件中转线程
在frameworks/base/services/java/com/android/server/keyinputqueue.java里创建了一个线程,它循环的读取事件,然后把事件放入事件队列里。
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); } } }; }
输入事件分发线程
在frameworks/base/services/java/com/android/server/windowmanagerservice.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
mqueue.getevent dispatchkey/dispatchpointer/dispatchtrackball
更多关于android相关内容感兴趣的读者可查看本站专题:《android开发入门与进阶教程》、《android调试技巧与常见问题解决方法汇总》、《android多媒体操作技巧汇总(音频,视频,录音等)》、《android基本组件用法总结》、《android视图view技巧总结》、《android布局layout技巧总结》及《android控件用法总结》
希望本文所述对大家android程序设计有所帮助。
上一篇: bzoj 3674(可持久化并查集)
下一篇: Global.asax取绝对路径的方法