这几天看了一直在看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()方法等待下一次键盘消息。