Android的事件有好几类,我们遇到最多的就是Touch事件。这部分和其他模块非常相似,系统有一个核心的Service来接收这些事件,通过IPC把事件分发用户进程,也就是相应的注册者,这部分虽然相似但也有不同。
通常情况Android的IPC采用的都是Binder,而在这里采用的是共享内存(Shared Memory)。为什么这么设计呢,实现过View的Touch事件的人都知道,touch的事件是非常频繁的,且要求实时性很高。而Binder是在共享内存基础上,加了一层安全性高、支持C/S的保护壳,所以它更加的heavy。对于频繁的内存操作,其效率不及共享内存。
那它是怎么实现C/S的呢?它采用的是pipe。pipe按道理不应该有2次数据拷贝吗?是的,但是如果传递只是一个字符,那就无关紧要了。能这么做是,事件处理的共享内存数据管理不涉及和内存的交互。也就是说共享内存和pipe实际是活在两个世界,它们的连接是通过2次单个字符数据的拷贝,这样效率就会比binder高不少。
事件采集:按键、touch、mouse这些硬件通过驱动把事件写入Linux的/dev/input目录下,不同的设备会在里面存入为event0、event1等这样的格式。
预处理:清理Android无用数据,保存有用数据。
WMS分发:有权利监听各种的事件,一般都是当前resume的view。哪个view是resume的呢?写的上一篇文章中《 Android View和Window的关系 》有提到,这个由WMS控制,因此有一个WMS分发的过程。
用户进程处理:也就是各种view的event dispatch。
事件Service也就是InputManagerService和其他Service启动一样,系统会率先孵化,在其start方法中,它首先会调用nativeStart,而在nativeStart中,我们直接看c代码吧
static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
status_t result = im->getInputManager()->start();
}
这一步调用了InputManager的start方法。
InputManager.cpp # start
status_t InputManager::start() {
status_t result = mDispatcherThread->run("InputDispatcher",
PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
result = mReaderThread->run("InputReader",
PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
}
这一步,InputManager开启了两个线程,一个Reader线程、一个Dispatch线程。顾名思义,一个读取、一个分发。读取是读取event节点中的数据,分发则是把事件发送给对应进程对应的接收者。
3.1 native的事件读取:
bool InputReaderThread::threadLoop() {
mReader->loopOnce();
}
InputReader#loopOnce
void InputReader::loopOnce() {
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
if (count) {
processEventsLocked(mEventBuffer, count);
}
}
可以看到其会通过EventHub去读去event节点中发生的事件次数,如果大于0,则执行processEventsLocked
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
for (const RawEvent* rawEvent = rawEvents; count;) {
int32_t type = rawEvent->type;
if (type < EventHubInterface::FIRST_SYNTHETIC_EVENT) {
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
case EventHubInterface::DEVICE_ADDED:
addDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::DEVICE_REMOVED:
removeDeviceLocked(rawEvent->when, rawEvent->deviceId);
break;
case EventHubInterface::FINISHED_DEVICE_SCAN:
handleConfigurationChangedLocked(rawEvent->when);
break;
}
}
}
}
这是对事件的第一步处理,遍历所有的事件,然后根据事件的类型做出相应的处理,普通的event会执行processEventForDeviceLocked把事件交给相应的deviceId。接下来的堆栈如下:
processEventForDeviceLocked
process
InputMapper->process
InputMapper->processKey
getListener()->notifyKey(&args);
这里它会调用getListener的来通知事件,而这里的listener则是在InputReader构造的时候的时候传入的Dispatcher。
InputReader构造函数
InputReader::InputReader(const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener) :
mContext(this), mEventHub(eventHub), mPolicy(policy),
mGlobalMetaState(0), mGeneration(1),
mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0)
InputManager构造函数
InputManager::InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
}
通过这两个构造函数可以清楚的看到,InputDispatch在InputReader注册了一个事件回调接口。所以Reader收到事件后经过一定的处理后,最终处理者还是dispatcher。
3.2 native的事件分发
接上一步,dispatcher收到回调后,会经过一系列堆栈后,最后走到dispatchKeyLocked方法中。
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) {
//判断条件,如文章所写。。。
Vector<InputTarget> inputTargets;
int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,
entry, inputTargets, nextWakeupTime);
if (injectionResult == INPUT_EVENT_INJECTION_PENDING) {
return false;
}
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
}
在这个方法中,要执行到以上代码,需经过一系列判断
1、检查事件是否重复
2、检查是否满足INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
3、给一个拦截事件的机会,比如说对于home键WMS的优先处理
4、判断是否丢弃事件
如果以上都不return,则会走如上的代码中的findFocusedWindowTargetLocked。
int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime,
const EventEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) {
if (! checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) {
injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED;
goto Failed;
}
reason = checkWindowReadyForMoreInputLocked(currentTime,
mFocusedWindowHandle, entry, "focused");
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
addWindowTargetLocked(mFocusedWindowHandle,
InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0),
inputTargets);
return injectionResult;
}
看上面代码,我们可以看到它这么执行:
1、检查权限
2、检查是否正在处理中
3、交给当前focus的handle处理
我们在这个方法先停一会儿,看看InputDispatcher是怎么获取到这个mFocusedWindowHandle呢?
显然,事件的处理者肯定是那些resume的view,所以开发人员在设计的时候,也肯定会在resume的时候把InputDispatcher传递进来。
上一篇文章中,我们知道当一个view resume的时候,WMS会调用addWindow方法。而在addWindow方法中,有一个很重要的代码,就是调用InputMonitor的updateInputWindowLw方法。InputMonitor的角色是WMS和InputDispatcher的中介。
public void updateInputWindowsLw(boolean force) {
if (inDrag) {
final InputWindowHandle dragWindowHandle = mService.mDragState.mDragWindowHandle;
if (dragWindowHandle != null) {
addInputWindowHandleLw(dragWindowHandle);
}
}
if (inPositioning) {
final InputWindowHandle dragWindowHandle = mService.mTaskPositioner.mDragWindowHandle;
if (dragWindowHandle != null) {
addInputWindowHandleLw(dragWindowHandle);
}
}
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
if (addInputConsumerHandle
&& inputWindowHandle.layer <= mService.mInputConsumer.mWindowHandle.layer) {
addInputWindowHandleLw(mService.mInputConsumer.mWindowHandle);
}
if (addWallpaperInputConsumerHandle) {
if (child.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
}
}
addInputWindowHandleLw(
inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);
}
}
if (addWallpaperInputConsumerHandle) {
addInputWindowHandleLw(mService.mWallpaperInputConsumer.mWindowHandle);
}
mService.mInputManager.setInputWindows(mInputWindowHandles);
}
这一步是把所有的可以接收事件的view的Handle通过addInputWindowHandlerLw加到mIntputWindowHandles这个成员数组变量中。最后调用setInputWindows,把数组交给native处理,也就是如下的代码。
public void setInputWindows(InputWindowHandle[] windowHandles) {
nativeSetInputWindows(mPtr, windowHandles);
}
static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobjectArray windowHandleObjArray) {
im->setInputWindows(env, windowHandleObjArray);
}
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
Vector<sp<InputWindowHandle> > windowHandles;
jsize length = env->GetArrayLength(windowHandleObjArray);
for (jsize i = 0; i < length; i++) {
sp<InputWindowHandle> windowHandle =
android_server_InputWindowHandle_getHandle(env, windowHandleObj);
windowHandles.push(windowHandle);
}
mInputManager->getDispatcher()->setInputWindows(windowHandles);
}
可以看到,WMS在addWindow的时候,最终会把所有的Window往InputManagerService塞,然后再交给native的InputDispatcher处理。
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
mWindowHandles = inputWindowHandles;
sp<InputWindowHandle> newFocusedWindowHandle;
for (size_t i = 0; i < mWindowHandles.size(); i++) {
const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
mWindowHandles.removeAt(i--);
continue;
}
if (windowHandle->getInfo()->hasFocus) {
newFocusedWindowHandle = windowHandle;
}
if (windowHandle == mLastHoverWindowHandle) {
foundHoveredWindow = true;
}
}
if (!foundHoveredWindow) {
mLastHoverWindowHandle = NULL;
}
if (mFocusedWindowHandle != newFocusedWindowHandle) {
mFocusedWindowHandle = newFocusedWindowHandle;
}
}
而InputDispatcher的处理也比较容易理解,遍历所有的WindowHandle,把最上层hasFocus的取出来,保存在上一步所说的mFocusedWindowHandle中,这样整个流程就通了。
主要的一些步骤就是:
1、InputManagerService启动
2、唤起InputReader和InputDispatcher,InputReader去不断轮询Event节点。
3、ViewRootImpl调用setView后,把当前Focus的Window信息交给WMS,再由WMS转交给IMS,最终交给InputDispatcher。
4、InputReader收到Event后,回调给InputDispatcher处理,再回调给InputMonitor,最后交给相应的view。
文章一开始提到,事件处理的IPC采用的是共享内存+pipe,这部分在哪儿体现呢?
我们回到WMS的addWindow方法,我们可以看到这么一段代码:
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
void openInputChannel(InputChannel outInputChannel) {
String name = makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
}
public static InputChannel[] openInputChannelPair(String name) {
return nativeOpenInputChannelPair(name);
}
可以看到,这边先为通道起名,然后调用native方法打开一个通道。在native端,它会调用InputTransport中的方法。
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
这块可以很清晰的看得到,分别设置了缓存区大小,然后开启了两个channel,而channel中的mFd实际上则是socket编号!
事件处理在Framework层的一些主要内容就是这些了
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。