这几天看了一直在看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; } }
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(); }
创建的。
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(); }
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); }
而已,而真正的逻辑控制是在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); }
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(); }
void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
mDispatcherThread进行保存,至此在java层的InputManager的实例化就完成了。
在WindowMangagerService的构造函数中,我们可以看见后来InputManager对象又调用了start()方法,这个方法就是通过层层调用去启动InputDispatchThread
和InputReaderThread线程,接下来我们就去看一下这两个线程的启动过程
public void start() { Slog.i(TAG, "Starting input manager"); nativeStart(); registerPointerSpeedSettingObserver(); registerShowTouchesSettingObserver(); updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); }
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."); } }
的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; }
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