zoukankan      html  css  js  c++  java
  • Android输入事件流程

    转载时请注明出处和作者联系方式
    文章出处:http://www.limodev.cn/blog
    作者联系方式:李先静 <xianjimli at hotmail dot com>

    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的结构。




    o 事件中转线程

    在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);
    }
    }
    };





    o 输入事件分发线程

    在frameworks/base/services/java/com/android/server/WindowManagerService.java里创建了一个输入事件分发线程,它负责把事件分发到相应的窗口上去。

    mQueue.getEvent
    dispatchKey/dispatchPointer/dispatchTrackball
  • 相关阅读:
    virtualbox+vagrant学习-2(command cli)-9-vagrant Plugin命令
    virtualbox+vagrant学习-2(command cli)-15-vagrant resume命令
    virtualbox+vagrant学习-2(command cli)-14-vagrant reload命令
    virtualbox+vagrant学习-2(command cli)-12-vagrant Provision命令
    virtualbox+vagrant学习-2(command cli)-11-vagrant PowerShell命令
    java设计模式之单例
    java设计模式之策略
    java设计模式之组合
    设计模式之适配器
    java设计模式之模板方法
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167534.html
Copyright © 2011-2022 走看看