作者: 李先静 日期: 2009-05-15 本文转自李先静的 blog,详细分析了 Android 的输入事件流程,非常详细。

EventHub

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 jbooleanandroid_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.getEventdispatchKey/dispatchPointer/dispatchTrackball

Reference

文章出处:http://www.limodev.cn/blog

作者联系方式:李先静 <xianjimli at hotmail dot com>

更多相关文章

  1. 11.GridView、ListView等的OnItemClick事件无法触发的解决方案。
  2. 自定义带提示的文本输入框
  3. android 【点击输入框调出输入法前的】输入框获取焦点和输入法的
  4. Android(安卓)WebView加载带有Input的输入框时点击无法弹出软键
  5. This version of the rendering library is more recent than yo
  6. ADB+adbWireless 抛开USB调试,接入无线调试
  7. Android(安卓)5.0(Lollipop)事件输入系统(Input System)
  8. android第四天晚:绘图和handle
  9. android手机连接到ubuntu方法

随机推荐

  1. xml配置的用法汇总
  2. 谈谈实现多渠道的实例教程
  3. 推荐10个常用的排序、分页用法
  4. 脚本控制的用法汇总
  5. 谈谈XMLTextReader的现状、前景与机遇
  6. 关于Xstream的7篇文章推荐
  7. 关于省份名称的详细介绍
  8. 关于XmlPullParser的5篇文章推荐
  9. 关于定义颜色的10篇文章推荐
  10. 关于XPath技术的详细介绍