Android 键盘事件处理过程

这几天看了一直在看android的按键事件处理的源码,当然不是拿起源码就啃,结合一些大牛的博客,顺着他们的思路以及这部分的源代码一路看下去,

刚开始看的时候思路非常的混乱,由于对C语言又不熟悉,所下载的android源码又不是同一个版本,看的很吃力,但是慢慢看着,也总结了一些学习规律

刚开始看的时候不必纠结于实现细节,先大致浏览一遍,在脑海中对一些类名函数名进行熟悉,对主要的类和函数的功能进行熟悉,然后用笔根据自己的

理解画出各个类之间的关系,先后顺序,将这个过程反复几遍就能够对android键盘事件的处理过程有一个较好的了解。下面根据这几天看的源码,写一篇

android键盘事件处理过程的博客,算是对自己思路的一个理顺吧。

step 1:在android键盘事件的处理过程中有几个非常重要的类,他们分别是:

1.WindowManagerService(这个是android窗口管理的服务类,在开机时他由系统服务SystemService进行初始化,他也负责android按键事件类的初始化)

2.InputManager(FrameWork层的C++类,负责管理所有的输入事件以及事件的转发)

3.InputManager(FrameWork层的java类,这个类主要负责对相应的C++类通过JNI方法进行调用,比如你按下一个按键最后肯定要调用到C++或C相关的一些

类,那么通过他其中的一些native方法就能够调用到)

4.InputReader(负责内核层消息的读取,事件转换以及事件分发,如内核层发过来一消息,InputReader需要识别这是哪一类的消息然后转给其他类进行处理)

5.InputDispatch(将InputReader类转发过来的消息分发给相应的窗口,并监控ANR)

6.EventHub(EventHub可以看作是输入消息的集散地,由于android中支持多种设备,而各种设备的消息类型可能又不一样,为了统一管理这些消息,所有的

消息都会通过EventHub进行收集并传递给InputReader,这样对于上层的程序来说就不需要关注底层设备的多样性,减少了上层逻辑的复杂性)

他们之间的关联关系如下图:


其中实线表示的是按键事件流的流动方向。虚线表示的是各个类之间的关联关系(有些关系没有进行注释了)

android 开机启动时,系统服务会初始化android窗口管理服务WindowManagerService,然后WindowMangerService会初始化InputManager,通过InputManager

去实例化一些按键处理的关键对象。下面我们就根据android的源码(版本为4.03)去看一看整个的初始化过程。

step1:WindowManagerService.main() 方法,WindowManagerService就是在这个类中进行实例化的

WindowManagerService的路径为:frameworks\base\services\java\com\android\server\wm\WindowMangerService.java

public static WindowManagerService main(Context context,
            PowerManagerService pm, boolean haveInputMethods, boolean allowBootMsgs) {
        WMThread thr = new WMThread(context, pm, haveInputMethods, allowBootMsgs);
        thr.start();

        synchronized (thr) {
            while (thr.mService == null) {
                try {
                    thr.wait();
                } catch (InterruptedException e) {
                }
            }
            return thr.mService;
        }
    }

在这个方法中,开启了一个线程WMThread,WindowMangerService就是在这个线程当中进行实例化的:

public void run() {
            Looper.prepare();
            WindowManagerService s = new WindowManagerService(mContext, mPM,
                    mHaveInputMethods, mAllowBootMessages);
            android.os.Process.setThreadPriority(
                    android.os.Process.THREAD_PRIORITY_DISPLAY);
            android.os.Process.setCanSelfBackground(false);

            synchronized (this) {
                mService = s;
                notifyAll();
            }

            // For debug builds, log event loop stalls to dropbox for analysis.
            if (StrictMode.conditionallyEnableDebugLogging()) {
                Slog.i(TAG, "Enabled StrictMode logging for WMThread's Looper");
            }

            Looper.loop();
        }

可以看见WindowManagerService在其中进行实例化了,并且用全局对象mService进行了保存,InputManager的实例化则是在WindowManagerService的构造函数中进行

创建的。

    private WindowManagerService(Context context, PowerManagerService pm,
            boolean haveInputMethods, boolean showBootMsgs) {
       .....

        mInputManager = new InputManager(context, this);

        PolicyThread thr = new PolicyThread(mPolicy, this, context, pm);
        thr.start();

        synchronized (thr) {
            while (!thr.mRunning) {
                try {
                    thr.wait();
                } catch (InterruptedException e) {
                }
            }
        }
        mInputManager.start();

    }

可以看见在WindowManagerService的构造函数中,InputManager在其中进行了实例化了,并且使用全局对象mInputManager进行了保存。接下来我们去看一下

InputManager的构造函数

InputManager的路径:frameworks\base\services\java\com\android\server\wm\InputManager.java

    public InputManager(Context context, WindowManagerService windowManagerService) {
        this.mContext = context;
        this.mWindowManagerService = windowManagerService;
        this.mCallbacks = new Callbacks();

        Looper looper = windowManagerService.mH.getLooper();

        Slog.i(TAG, "Initializing input manager");
        nativeInit(mContext, mCallbacks, looper.getQueue());

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
    }

在他的构造函数中,我们发现windowManagerService的对象当作参数传了进来,调用了nativiInit()方法,在上面类的介绍中我们知道,java层的InputManager只是调用的接口

而已,而真正的逻辑控制是在C++语言写的InputManager这个类中。nativiInit()方法是通过JNI调用其它类中的方法,它的路径为:

frameworks\base\services\jni\com_android_server_InputManager.cpp\android_server_InputManager_nativeInit

static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
        jobject contextObj, jobject callbacksObj, jobject messageQueueObj) {
    if (gNativeInputManager == NULL) {
        sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
        gNativeInputManager = new NativeInputManager(contextObj, callbacksObj, looper);
    } else {
        LOGE("Input manager already initialized.");
        jniThrowRuntimeException(env, "Input manager already initialized.");
    }
}

在这个方法中我们可以看见,创建了一个NativeInputManager的实例,接下来我们去看一下NativeInputManager,它的路径为:

frameworks\base\services\jni\com_android_server_InputManager.cpp\android_server_InputManager_nativeInit\NativeInputManager.cpp

NativeInputManager::NativeInputManager(jobject contextObj,
        jobject callbacksObj, const sp<Looper>& looper) :
        mLooper(looper) {
    JNIEnv* env = jniEnv();

    mContextObj = env->NewGlobalRef(contextObj);
    mCallbacksObj = env->NewGlobalRef(callbacksObj);

	....

    sp<EventHub> eventHub = new EventHub();
    mInputManager = new InputManager(eventHub, this, this);
}

从他的构造函数中我们可以看见,在这里创建了EventHub实例,然后将EventHub实例作为参数传给InputManager,创建InputManager实例。接下来我们就去看一下

InputManager的构造函数,InputManager的路径为:

frameworks\base\services\input\InputManager.cpp

InputManager::InputManager(
        const sp<EventHubInterface>& eventHub,
        const sp<InputReaderPolicyInterface>& readerPolicy,
        const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
    mDispatcher = new InputDispatcher(dispatcherPolicy);
    mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
    initialize();
}

在他的构造函数中我们可以看见,创建了InputDispatcher和InputReader对象,并且调用了initialize()方法。

void InputManager::initialize() {
    mReaderThread = new InputReaderThread(mReader);
    mDispatcherThread = new InputDispatcherThread(mDispatcher);
}

在initialize函数中我们可以看见,创建了InputReaderThread线程实例以及InputDispatchThread实例,并且分别用全局对象mReaderThread和

mDispatcherThread进行保存,至此在java层的InputManager的实例化就完成了。

在WindowMangagerService的构造函数中,我们可以看见后来InputManager对象又调用了start()方法,这个方法就是通过层层调用去启动InputDispatchThread

和InputReaderThread线程,接下来我们就去看一下这两个线程的启动过程

    public void start() {
        Slog.i(TAG, "Starting input manager");
        nativeStart();

        registerPointerSpeedSettingObserver();
        registerShowTouchesSettingObserver();

        updatePointerSpeedFromSettings();
        updateShowTouchesFromSettings();
    }

在InputManager的start方法中我们可以看见,调用了nativStart()方法,这是一个JNI方法,它的路径为:

frameworks\base\services\jni\com_android_server_InputManager.cpp\android_server_InputManager_nativeStart

static void android_server_InputManager_nativeStart(JNIEnv* env, jclass clazz) {
    if (checkInputManagerUnitialized(env)) {
        return;
    }

    status_t result = gNativeInputManager->getInputManager()->start();
    if (result) {
        jniThrowRuntimeException(env, "Input manager could not be started.");
    }
}

在这个方法中我们可以看见,通过NativeInputManager对象调用getInputManager()方法获取InputManager对象,然后通过InputManager对象调用InputManager类中

的start()方法,接下来我们就去看一下InputManager类中的start方法

status_t InputManager::start() {
    status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
    if (result) {
        LOGE("Could not start InputDispatcher thread due to error %d.", result);
        return result;
    }

    result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
    if (result) {
        LOGE("Could not start InputReader thread due to error %d.", result);

        mDispatcherThread->requestExit();
        return result;
    }

    return OK;
}

在start方法中我们可以看见,InputDispatcherThread线程对象mDispatcherThread启动了线程,InputReader线程对象启动了InputReaderThread线程

InputReaderThread 线程启动后就进入了InputReader中的threadLoop()方法

bool InputReaderThread::threadLoop() {
    mReader->loopOnce();
    return true;
}
在这里调用了loopOnce()方法

void InputReader::loopOnce() {
    ......

    size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

    { // acquire lock
        AutoMutex _l(mLock);

        if (count) {
            processEventsLocked(mEventBuffer, count);
        }
        if (!count || timeoutMillis == 0) {
            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
#if DEBUG_RAW_EVENTS
            LOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
#endif
            mNextTimeout = LLONG_MAX;
            timeoutExpiredLocked(now);
        }
    } // release lock
    mQueuedListener->flush();
}
在loopOnce()方法中我们可以看见,EventHub对象调用了getEvents()方法,getEvents方法就是监听所有的Event输入

size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
    LOG_ASSERT(bufferSize >= 1);

    AutoMutex _l(mLock);

    struct input_event readBuffer[bufferSize];

    RawEvent* event = buffer;
    size_t capacity = bufferSize;
    bool awoken = false;
    for (;;) {
        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);

        // Reopen input devices if needed.
        if (mNeedToReopenDevices) {
            mNeedToReopenDevices = false;

            LOGI("Reopening all input devices due to a configuration change.");

            closeAllDevicesLocked();
            mNeedToScanDevices = true;
            break; // return to the caller before we actually rescan
        }

        // Report any devices that had last been added/removed.
        while (mClosingDevices) {
            Device* device = mClosingDevices;
            LOGV("Reporting device closed: id=%d, name=%s\n",
                 device->id, device->path.string());
            mClosingDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_REMOVED;
            event += 1;
            delete device;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

        if (mNeedToScanDevices) {
            mNeedToScanDevices = false;
            scanDevicesLocked();
            mNeedToSendFinishedDeviceScan = true;
        }

        while (mOpeningDevices != NULL) {
            Device* device = mOpeningDevices;
            LOGV("Reporting device opened: id=%d, name=%s\n",
                 device->id, device->path.string());
            mOpeningDevices = device->next;
            event->when = now;
            event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;
            event->type = DEVICE_ADDED;
            event += 1;
            mNeedToSendFinishedDeviceScan = true;
            if (--capacity == 0) {
                break;
            }
        }

        if (mNeedToSendFinishedDeviceScan) {
            mNeedToSendFinishedDeviceScan = false;
            event->when = now;
            event->type = FINISHED_DEVICE_SCAN;
            event += 1;
            if (--capacity == 0) {
                break;
            }
        }

        // Grab the next input event.
        bool deviceChanged = false;
        while (mPendingEventIndex < mPendingEventCount) {
            const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++];
            if (eventItem.data.u32 == EPOLL_ID_INOTIFY) {
                if (eventItem.events & EPOLLIN) {
                    mPendingINotify = true;
                } else {
                    LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events);
                }
                continue;
            }

            if (eventItem.data.u32 == EPOLL_ID_WAKE) {
                if (eventItem.events & EPOLLIN) {
                    LOGV("awoken after wake()");
                    awoken = true;
                    char buffer[16];
                    ssize_t nRead;
                    do {
                        nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
                    } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
                } else {
                    LOGW("Received unexpected epoll event 0x%08x for wake read pipe.",
                            eventItem.events);
                }
                continue;
            }

            ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32);
            if (deviceIndex < 0) {
                LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.",
                        eventItem.events, eventItem.data.u32);
                continue;
            }

            Device* device = mDevices.valueAt(deviceIndex);
            if (eventItem.events & EPOLLIN) {
                int32_t readSize = read(device->fd, readBuffer,
                        sizeof(struct input_event) * capacity);
                if (readSize == 0 || (readSize < 0 && errno == ENODEV)) {
                    // Device was removed before INotify noticed.
                    LOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n",
                         device->fd, readSize, bufferSize, capacity, errno);
                    deviceChanged = true;
                    closeDeviceLocked(device);
                } else if (readSize < 0) {
                    if (errno != EAGAIN && errno != EINTR) {
                        LOGW("could not get event (errno=%d)", errno);
                    }
                } else if ((readSize % sizeof(struct input_event)) != 0) {
                    LOGE("could not get event (wrong size: %d)", readSize);
                } else {
                    int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id;

                    size_t count = size_t(readSize) / sizeof(struct input_event);
                    for (size_t i = 0; i < count; i++) {
                        const struct input_event& iev = readBuffer[i];
                        LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d",
                                device->path.string(),
                                (int) iev.time.tv_sec, (int) iev.time.tv_usec,
                                iev.type, iev.code, iev.value);

#ifdef HAVE_POSIX_CLOCKS
                        event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL
                                + nsecs_t(iev.time.tv_usec) * 1000LL;
                        LOGV("event time %lld, now %lld", event->when, now);
#else
                        event->when = now;
#endif
                        event->deviceId = deviceId;
                        event->type = iev.type;
                        event->scanCode = iev.code;
                        event->value = iev.value;
                        event->keyCode = AKEYCODE_UNKNOWN;
                        event->flags = 0;
                        if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) {
                            status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code,
                                        &event->keyCode, &event->flags);
                            LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n",
                                    iev.code, event->keyCode, event->flags, err);
                        }
                        event += 1;
                    }
                    capacity -= count;
                    if (capacity == 0) {
                        // The result buffer is full.  Reset the pending event index
                        // so we will try to read the device again on the next iteration.
                        mPendingEventIndex -= 1;
                        break;
                    }
                }
            } else {
                LOGW("Received unexpected epoll event 0x%08x for device %s.",
                        eventItem.events, device->identifier.name.string());
            }
        }
        if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) {
            mPendingINotify = false;
            readNotifyLocked();
            deviceChanged = true;
        }

        // Report added or removed devices immediately.
        if (deviceChanged) {
            continue;
        }

        // Return now if we have collected any events or if we were explicitly awoken.
        if (event != buffer || awoken) {
            break;
        }

        mPendingEventIndex = 0;

        mLock.unlock(); // release lock before poll, must be before release_wake_lock
        release_wake_lock(WAKE_LOCK_ID);

        int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);

        acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
        mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock

        if (pollResult == 0) {
            // Timed out.
            mPendingEventCount = 0;
            break;
        }

        if (pollResult < 0) {
            // An error occurred.
            mPendingEventCount = 0;

            // Sleep after errors to avoid locking up the system.
            // Hopefully the error is transient.
            if (errno != EINTR) {
                LOGW("poll failed (errno=%d)\n", errno);
                usleep(100000);
            }
        } else {
            // Some events occurred.
            mPendingEventCount = size_t(pollResult);


            if (mNumCpus > 1) {
                usleep(250);
            }
        }
    }

    // All done, return the number of events we read.
    return event - buffer;
}
首先使用一个没有退出条件的for(;;)循环,不断检测是否有设备关闭或打开直至有数据返回。然后检测是否有需要关闭的设备,如果有则将其关闭,然后将

mNeedToScanDevices设置为true,则表示关闭设备后,则需要检测是否有设备打开,然后将前面关闭的设备进行上报,没有则不需进行上报,然后进行设备的扫描

如果有打开的设备,则将事件进行上报,然后停止检测设备的打开情况。最后检测是否还有未处理的输入设备事件发生了。

InputDispatcherThread启动后,进入InputDispatcher类中的threadLoop()方法

bool InputDispatcherThread::threadLoop() {
    mDispatcher->dispatchOnce();
    return true;
}
在这个方法中,其调用了dispatchOnce()方法。

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    { // acquire lock
        AutoMutex _l(mLock);
        dispatchOnceInnerLocked(&nextWakeupTime);

        if (runCommandsLockedInterruptible()) {
            nextWakeupTime = LONG_LONG_MIN;  // force next poll to wake up immediately
        }
    } // release lock

    // Wait for callback or timeout or wake.  (make sure we round up, not down)
    nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}
在这个方法里我们可以知道,键盘消息是通过dispatchOnceInnerLocked方法来处理的,处理完毕之后调用pollOnce()方法等待下一次键盘消息。

参考博客:

http://www.cnblogs.com/lcw/p/3506110.html

http://www.cnblogs.com/samchen2009/p/3368158.html

http://blog.csdn.net/luoshengyang/article/details/6882903#quote

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 系统的键盘事件 是由InputManagerService 来监控的, 而InputManagerService 是由 SystemServer 来启动的 创建了一个类的对象 WindowInputEvent Receiver InputEventRe ceiver的构造 函数 WindowInputEventReceiver extends InputEventReceiver nativeInit InputEventReceiver.cpp nativeInit android_view_InputE ventReceiver.cpp NativeInputE ventReceiver 的构造函数 LooperCallback: :handleEvent NativeInputEve ntReceiver::ha ndleEvent Looper::p ollInner Looper::p ollOnce NativeInputEventRecei ver : public LooperCallback 有方法 NativeInputEventReceiver:: handleEvent 没有键盘事件发生, InputManager 中的InputReader 正在睡眠等待键盘事件 的发生,而InputManager 中的 InputDispatcher 正在等待InputReader 从睡眠中醒过来并且唤醒它,而应用程 序也正在消息循环中等待InputDispatcher 从睡眠中醒过来并且唤醒它。 分析InputManager 分发键盘消息给应用程序的过程 InputReader .pollOnce EventHub.g etEvent InputReader::pr ocessEventsLock ed InputReader::pr ocessEventsForD eviceLocked InputDevic e::process 没有键盘事件发生, InputReaderThread 线程就会睡眠在EventHub.getEvent 函数 上,当键盘事件发生后,就会把这个事件封 装成一个RawEvent 对象,然后返回到 pollOnce函数中,执行此函数 有键盘事件 InputReader.cpp InputMapper::process( 这里 是KeyboardInputMapper) InputReader::createDeviceLock ed中根据类型创建的,负责处理轨迹 球事件的TrackballInputMapper 对 象以及负责处理触摸屏事件的 TouchInputMapper 对象等 KeyboardInputMap per::processKey 这个函数首先对对按 键作一些处理,例如 需要根据当时屏幕的 方向来调整键盘码 InputDispatcher::notify Key( 继承自 InputListenerInterface) KeyboardInputMappge r函数通知 InputDispatcher ,有 键盘事件发生了 1. 调用 validateKeyEvent 函数来验 证action参数是否正确 2. 参数action检查通过后,还通过 policyFlags 参数来检查一下同时是 否有ALT和SHIFT键被按下 3. 调用 enqueueInboundEventLocked 函数 把这个按键事件封装成一

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值