Android输入设备处理流程
来源:互联网 发布:vb下拉菜单非活性 编辑:程序博客网 时间:2024/04/24 22:14
原文地址::http://hi.baidu.com/viataa/blog/item/5a179f8fe349876f9e2fb4af.html
Android系统硬件的访问是通过HAL(Hardware Abstract Layer)来进行的,HAL就起到来Hardware adapter的作用。对于输入设备,如KeyPad、TouchPanel等,Android用EventHub具体的对其进行了封装。
输入设备驱动程序对用户空间应用程序提供一些设备文件,这些设备文件放在/dev/input里面。
1. 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;}int EventHub::scan_dir(const char *dirname){ char devname[PATH_MAX]; char *filename; DIR *dir; struct dirent *de; dir = opendir(dirname); if(dir == NULL) return -1; strcpy(devname, dirname); filename = devname + strlen(devname); *filename++ = '/'; while((de = readdir(dir))) { if(de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue; strcpy(filename, de->d_name); open_device(devname); } closedir(dir); return 0;}
}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来改变键值的映射关系。
2. JNI函数
在frameworks/base/services/jni/com_android_server_KeyInputQueue.cpp文件 中,向JAVA提供了函数android_server_KeyInputQueue_readEvent,用于读取输入设备事件。
static jbooleanandroid_server_KeyInputQueue_readEvent(JNIEnv* env, jobject clazz, jobject event){ gLock.lock(); sp<EventHub> 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的结构。
3. 事件中转线程
在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); } } }};注:readEvent就是调用了JNI的android_server_KeyInputQueue_readEven函数
4. 输入事件分发线程
在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。
在WindowManagerService构造函数中调用
mInputThread = new InputDispatcherThread();
mInputThread.start();
private final class InputDispatcherThread extends Thread{ // Time to wait when there is nothing to do: 9999 seconds. static final int LONG_WAIT=9999*1000; public InputDispatcherThread() { super("InputDispatcher"); } @Override public void run() { while (true) process(); } private void process() { android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_URGENT_DISPLAY); ... ... while (true) { long curTime = SystemClock.uptimeMillis(); // Retrieve next event, waiting only as long as the next // repeat timeout. If the configuration has changed, then // don't wait at all -- we'll report the change as soon as // we have processed all events. QueuedEvent ev = mQueue.getEvent( (int)((!configChanged && curTime < nextKeyTime) ? (nextKeyTime-curTime) : 0)); ... ... try { if (ev != null) { switch (ev.classType) { case RawInputEvent.CLASS_KEYBOARD: ... dispatchKey((KeyEvent)ev.event, 0, 0); mQueue.recycleEvent(ev); break; case RawInputEvent.CLASS_TOUCHSCREEN: //Slog.i(TAG, "Read next event " + ev); dispatchPointer(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_TRACKBALL: dispatchTrackball(ev, (MotionEvent)ev.event, 0, 0); break; case RawInputEvent.CLASS_CONFIGURATION_CHANGED: configChanged = true; break; default: mQueue.recycleEvent(ev); break; } } else if (configChanged) { configChanged = false; sendNewConfiguration(); } else if (lastKey != null) { curTime = SystemClock.uptimeMillis(); ... ... dispatchKey(newEvent, 0, 0); } else { curTime = SystemClock.uptimeMillis(); lastKeyTime = curTime; nextKeyTime = curTime + LONG_WAIT; } } catch (Exception e) { ... ... } } }
- Android输入设备处理流程
- Android phone在拨号盘输入*#06#的处理流程
- Android处理输入事件的流程(一)
- Android设备运行流程
- Android新增输入设备
- android 模拟输入设备
- Android事件处理分析+Android事件处理 +Android输入事件流程
- Android事件处理分析+Android事件处理 +Android输入事件流程
- Android输入事件流程
- Android输入事件流程
- Android输入事件流程
- Android 输入事件流程
- Android输入事件流程
- Android 输入事件流程
- Android输入事件流程
- Android 输入事件流程
- android源码分析——事件输入流程MotionEvent事件处理流程
- Android 输入管理服务-输入事件到达之后的处理流程
- DELPHI XE2马上推出
- Struts1.x系列教程(3):属性(资源)文件乱码问题的解决之道
- 正则表达式学习
- XHTML 与 HTML 的区别是什么?
- MSBuild + MSILInect实现编译时AOP-改变前后对比
- Android输入设备处理流程
- PostSharp - Thread Dispatching(GUI多线程)
- Foundation框架,几乎为我们提供了日常需要使用的所有核心实用工具类---之一
- 十道海量数据处理面试题与十个方法大总结
- 判断一个类是JAVA类型还是用户定义类型
- php.ini配置文件详解
- [Android]ListView性能优化之视图缓存
- 即时通讯 IM+视频会议+H323+监控系统设计概览
- Servlet过滤器