android应用程序键盘事件机制
启动初始化
Android的键盘事件由InputManager监控。
先来看下InputManager是如何启动的。
Android在启动的时候,Zygote会启动SystemServer.java
目录:frameworks/base/services/java/com/android/server/SystemServer.java
class ServerThread { …….. public voidinitAndLoop() { ……. // Create a handler thread just for thewindow manager to enjoy. HandlerThread wmHandlerThread = newHandlerThread("WindowManager"); wmHandlerThread.start(); Handler wmHandler = newHandler(wmHandlerThread.getLooper()); …….inputManager = new InputManagerService(context, wmHandler); Slog.i(TAG, "Window Manager"); wm = WindowManagerService.main(context, power, display, inputManager, wmHandler, factoryTest !=SystemServer.FACTORY_TEST_LOW_LEVEL, !firstBoot, onlyCore); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ServiceManager.addService(Context.INPUT_SERVICE, inputManager); ActivityManagerService.self().setWindowManager(wm); inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); ………. }}
SystemServer在初始化的时候,会调用WindowManagerServer的main函数。同时,初始化InputManagerService并启动InputManagerService。在这里将wmHandler的句柄传给了InputManagerServer。
那么,在WindowManagerServer.main又做了什么?
frameworks/base/services/java/com/android/server/wm./WindowManagerServer.javapublic static WindowManagerService main(final Context context, final PowerManagerService pm, final DisplayManagerService dm, final InputManagerService im, final Handler wmHandler, final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore) { final WindowManagerService[] holder = new WindowManagerService[1]; wmHandler.runWithScissors(new Runnable() { @Override public void run() { holder[0] = newWindowManagerService(context, pm, dm, im, haveInputMethods,showBootMsgs, onlyCore); } }, 0); return holder[0];}
这里main,实例化了一个WindowManagerServer。这里传入的inputmanager是怎么用的呢?InputManager在4.4.4中,在WindowmanagerServer中InputManager提供状态以及UI的更新调用。有兴趣的读者,可以瞄一眼
InputManagerService.java
看一下InputManagerService.java的构造体
frameworks/base/core/java/android/hardware/input/InputManagerService.java
public classInputManagerServer { …….. public InputManagerService(Context context,Handler handler) { this.mContext = context; this.mHandler = new InputManagerHandler(handler.getLooper()); …….. mPtr = nativeInit(this, mContext,mHandler.getLooper().getQueue()); } ……..}
InputManagerServer是由SystemServer直接启动的。
Handler是什么呢?
之前说这个从SystemServer传进来的。换言之,这里的Looper跟SystemServer传入的Looper是一样的,也就是WindowManager跟InputManager用了一个MessageQueue。那么,它在某种意义上算是消息同步的。
nativeInit是native层的方法,将结果又传递给java层的mPtr保存起来,在后续一定会有相关的转换调用。
nativeInit在com_android_server_input_InputManagerService.cpp中
frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
static jint nativeInit(JNIEnv* env, jclass clazz, jobject serviceObj,jobject contextObj, jobject messageQueueObj) { spmessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL){ jniThrowRuntimeException(env, "MessageQueue is notinitialized."); return 0; } NativeInputManager* im = newNativeInputManager(contextObj, serviceObj, messageQueue->getLooper()); im->incStrong(0); return reinterpret_cast(im);}
这里又将java层传入的MessageQueue转换,再次初始化NativeInputManager。
NativeInputManager::NativeInputManager(jobjectcontextObj, jobject serviceObj, constsp& looper) : mLooper(looper) { ……. sp eventHub = newEventHub(); mInputManager = new InputManager(eventHub,this, this);}
将looper保存到native层的mLooper。实例话了EventHub,将其作为参数传递给InputManager实例化。
这里有必要看一下EventHub的结构体。
EventHub::EventHub(void):
mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1),mControllerNumbers(),
mOpeningDevices(0), mClosingDevices(0),
mNeedToSendFinishedDeviceScan(false),
mNeedToReopenDevices(false),mNeedToScanDevices(true),
mPendingEventCount(0),mPendingEventIndex(0), mPendingINotify(false) {
acquire_wake_lock(PARTIAL_WAKE_LOCK,WAKE_LOCK_ID);
mEpollFd =epoll_create(EPOLL_SIZE_HINT);
mINotifyFd =inotify_init();
int result =inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE);
struct epoll_event eventItem;
memset(&eventItem, 0,sizeof(eventItem));
eventItem.events =EPOLLIN;
eventItem.data.u32= EPOLL_ID_INOTIFY;
result =epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem);
int wakeFds[2];
result =pipe(wakeFds);
mWakeReadPipeFd =wakeFds[0];
mWakeWritePipeFd =wakeFds[1];
result = fcntl(mWakeReadPipeFd, F_SETFL,O_NONBLOCK);
result =fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
eventItem.data.u32= EPOLL_ID_WAKE;
result =epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}
看标红的代码段:EventHub使用的epoll机制,inotify机制,pipe机制。
这段结构体有如下解释
1. 对一些成员变量做了初始化
2. 创建了epoll管道,EPOLL_SIZE_HINT代表的是最大监控数量
3. 调用inotify机制对/dev/input文件目录进行监听,任何增删改动作
4. 用epoll机制EPOLL_CTL_ADD命令把inotify的句柄和以及后面一句中的mWakeReadPipeFd句柄,加入到epoll的句柄mEpollFd中。这样,只要/dev/input下面有任何动作,就会在mWakeWritePipeFd中写数据,唤醒线程,epoll_wait就会被返回。
InputManager.cpp
frameworks/base/services/input/InputManager.cpp
InputManager::InputManager( const sp&eventHub, constsp& readerPolicy, constsp& dispatcherPolicy) { mDispatcher = newInputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub,readerPolicy, mDispatcher); initialize();}
这里实例化了InputDispatcher和InputReader。
InputDispatcher:从这里看不出具体的,猜测是按键消息的分发给当前激活的activity。比如dispatchTouchEvent之类就是分发
InputReader:同样看不出来,但是,结合eventHub,readerPolicy,可能是读取键盘事件。
事实证明猜测是正确的。后续分析。。。。
voidInputManager::initialize() { mReaderThread = newInputReaderThread(mReader); mDispatcherThread = newInputDispatcherThread(mDispatcher);}
这里,又将实例化的InputDispatcher和InputReader,再次传给InputDispatcherThread和InputReaderThread作为参数。这里的两个线程实例一个是用来读取键盘事件,而另一个是用来分发键盘事件的消息。
至此,键盘事件的初始化就完成了。
文件结构如下:
小结:
1. SystemServer在initandloop中实例化了InputManagerServer,这是用来接收分派按键事件的service
2. SystemServer将InputManagerServer传递给WindowManagerService控制输入法的显示,以及获取相关状态
3. C++层也相应地创建一个InputManager的实例会监控处理键盘事件
4. InputManager用InputReader和InputDispatcher分别处理键盘事件的读取以及消息分发
5. InputReader和InputDispatcher各自有一个线程来做相关的工作
事件分发和获取
对整体的结构有个大概的样子之后,开始了解键盘事件的处理方法
重新回到SystemServer
frameworks/base/services/java/com/android/server/SystemServer.java
Class SystemServer{ Publicvoid initAndLoop(){ …….. inputManager= new InputManagerService(context, wmHandler); ……… inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start();……}}
InputManagerServer被实例化后,又被调用了start方法。
来看下start方法
切换到InputManagerServer.java
frameworks/base/core/java/android/hardware/input/InputManagerService.java
public void start() { Slog.i(TAG, "Starting input manager"); nativeStart(mPtr); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); registerPointerSpeedSettingObserver(); registerShowTouchesSettingObserver(); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); updatePointerSpeedFromSettings(); updateShowTouchesFromSettings();}
这里,将直接init出来的mPtr作为参数传递给native层的nativestart方法。
registerPointerSpeedSettingObserver:这是监听Settings.System.POINTER_SPEED,一旦改变就修改对应的触摸监听频率
registerShowTouchesSettingObserver:这是监听Settings.System.SHOW_TOUCHES,这个具体是什么用的。不详。有兴趣的读者可以了解下。
这里同时监听了Intent.ACTION_USER_SWITCHED,若用户切换,那么重新调用updatePointerSpeedFromSettings(); updateShowTouchesFromSettings();
updatePointerSpeedFromSettings();:更新系统属性值Settings.System.POINTER_SPEED,并设置
updateShowTouchesFromSettings();:更新系统属性值Settings.System.SHOW_TOUCHES,并设置
重点看一下nativeStart,其他的都是浮云啊。
frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
…inlinesp getInputManager() const { return mInputManager; }…….static voidnativeStart(JNIEnv* env, jclass clazz, jint ptr) { NativeInputManager* im =reinterpret_cast(ptr); status_t result =im->getInputManager()->start(); if (result) { jniThrowRuntimeException(env,"Input manager could not be started."); }}……….
这里将java的mPtr转换成NativeInputManager,继而获得在native层保存的mInputManager,调用其start方法。
继续看InputManager.cpp
status_tInputManager::start() { status_t result =mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY); ………. result =mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); ……..}
这里的start方法,将mDispatcherThread和mReaderThread运行起来,同时给了PRIORITY_URGENT_DISPLAY这个级别跟SurfaceFlinger的priority是一样.可见是及时响应的
接下来看下,InputDispatcherThread
frameworks/base/services/input/InputDispatcher.cpp
bool InputDispatcherThread::threadLoop() { mDispatcher->dispatchOnce(); return true;}
为什么这个Thread这么奇葩,因为android对thread有封装,有兴趣的话,可以了解下。
这里mDispatcher就是在实例化InputDispatcherThread作为参数传入的InputDispatcher。
void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; {// acquire lock AutoMutex _l(mLock); mDispatcherIsAliveCondition.broadcast(); // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { dispatchOnceInnerLocked(&nextWakeupTime); } // Run all pending commands if there are any. // If any commands were run then force the next poll to wake upimmediately. if (runCommandsLockedInterruptible()) { nextWakeupTime = LONG_LONG_MIN; } }// 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:获取键盘事件,如果没有消息的话就是阻塞,底层的实现是基于pipe机制的。
小结下,InputDispatch会调用dispatchOnceInnerLocked去分发消息;在消息分完完成后,又使用mLooper继续获取消息。当mLooper.pollOnce有读取到消息的时候就会往管道里头写新的内容,唤醒正在等待键盘事件的线程;当没有消息的时候,就无限的阻塞着,线程进入空闲等待状态。
事件读取
先说InputReader。为什么先这个,因为这个短小精悍,InputDispatcher太长了。处理了InputReader会对后续InputDispatcher也有帮助。
InputReaderThread
frameworks/base/services/input/ InputReaderThread.cpp
boolInputReaderThread::threadLoop() { mReader->loopOnce(); return true;}
这里调用了InputReader的loopOnce方法。
void InputReader::loopOnce() { …………. size_t count = mEventHub->getEvents(timeoutMillis,mEventBuffer, EVENT_BUFFER_SIZE); {// acquire lock ………. if (count) { processEventsLocked(mEventBuffer, count); }………………… }}
这里,从mEventHub中获得了event并存在mEventBuffer中,然后,将mEventBuffer和count作为参数传递给processEventsLocked处理。
看下getEvent是怎么获取的?
/frameworks/base/services/input/EventHub.cpp
size_tEventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { ALOG_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;………… closeAllDevicesLocked();//#~~~~~~~~~~~~~~~~~~~~~~~mark mNeedToScanDevices = true; break; // return to the callerbefore we actually rescan }//#这里的mNeedToReopenDevices标记为true,那么就关闭所有的设备,并标记mNeedToScanDevices为true,重新扫描设备 // Report any devices that had lastbeen added/removed. while (mClosingDevices) { Device* device = mClosingDevices; ALOGV("Reporting deviceclosed: id=%d, name=%s\n", device->id,device->path.string()); mClosingDevices = device->next; event->when = now; event->deviceId = device->id== mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id; event->type = DEVICE_REMOVED; event += 1; delete device; mNeedToSendFinishedDeviceScan =true; if (--capacity == 0) { break; } }//#若mClosingDevices不为空,那么将所有的mClosingDevices重置,并删除。 if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked();//#~~~~~~~~~~~~~~~~~~~~~~~mark mNeedToSendFinishedDeviceScan =true; }//#若mNeedToScanDevices判定为true,那么调用scanDevicesLocked while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; ALOGV("Reporting deviceopened: 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 fd, readBuffer, sizeof(struct input_event)* capacity); if (readSize == 0 || (readSize< 0 && errno == ENODEV)) { // Device was removedbefore INotify noticed. ALOGW("could not getevent, 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) { ALOGW("could notget event (errno=%d)", errno); } } else if ((readSize %sizeof(struct input_event)) != 0) { ALOGE("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 path.string(), (int)iev.time.tv_sec, (int) iev.time.tv_usec, iev.type,iev.code, iev.value); ……….. if (iev.type == EV_MSC){ if (iev.code == MSC_ANDROID_TIME_SEC){ device->timestampOverrideSec = iev.value; continue; } else if (iev.code== MSC_ANDROID_TIME_USEC) { device->timestampOverrideUsec= iev.value; continue; } } if(device->timestampOverrideSec || device->timestampOverrideUsec) { iev.time.tv_sec =device->timestampOverrideSec; iev.time.tv_usec =device->timestampOverrideUsec; if (iev.type ==EV_SYN && iev.code == SYN_REPORT) { device->timestampOverrideSec = 0; device->timestampOverrideUsec = 0; } ALOGV("appliedoverride time %d.%06d", int(iev.time.tv_sec), int(iev.time.tv_usec)); } #ifdefHAVE_POSIX_CLOCKS ……….#else event->when = now;#endif event->deviceId =deviceId; event->type =iev.type; event->code =iev.code; event->value =iev.value; event += 1; capacity -= 1; } if (capacity == 0) { // The result buffer isfull. Reset the pending event index // so we will try toread the device again on the next iteration. mPendingEventIndex -=1; break; } } } else if (eventItem.events &EPOLLHUP) { ALOGI("Removing device %sdue to epoll hang-up event.", device->identifier.name.string()); deviceChanged = true; closeDeviceLocked(device); } else { ALOGW("Received unexpectedepoll 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 devicesimmediately. if (deviceChanged) { continue; } // Return now if we have collected anyevents or if we were explicitly awoken. if (event != buffer || awoken) { break; } ……………….. mPendingEventIndex = 0; mLock.unlock(); // release lock beforepoll, 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 afterpoll, 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 avoidlocking up the system. // Hopefully the error istransient. if (errno != EINTR) { ALOGW("poll failed(errno=%d)\n", errno); usleep(100000); } } else { // Some events occurred. mPendingEventCount =size_t(pollResult); } } // All done, return the number of events weread. return event - buffer;}
函数相当的长,一步步来分析。
一.重新打开设备
先判断是否有必要重新打开设备。
// Reopen input devices if needed. if (mNeedToReopenDevices) { mNeedToReopenDevices = false;………… closeAllDevicesLocked(); mNeedToScanDevices = true; break; // return to the callerbefore we actually rescan } voidEventHub::closeAllDevicesLocked() { while (mDevices.size() > 0) { closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1)); }}
遍历了所有mDevices,只要关掉一个mDevices的size就会减1.所以,遍历就出来了
void EventHub::closeDeviceLocked(Device*device) {
……….
if (!device->isVirtual()) {
if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL,device->fd, NULL)) {
ALOGW("Could not remove device fd from epoll instance. errno=%d", errno);
}
}
releaseControllerNumberLocked(device);
mDevices.removeItem(device->id);
device->close();
………
}
这里聚焦在跟设备相关的上,在关闭设备的时候,通过epoll机制,将EPOLL_CTL_DEL,以及mEpollFd作为参数传递给epoll_ctl,将关闭epoll通道。然后,从mDevices中移除当前正在删除的device,并将索引重定位。这样设备的删除就完成了。
二.释放被关闭的设备
同步正在被关闭的设备,将device结构体重置后,删除释放。
while (mClosingDevices) { Device* device = mClosingDevices; mClosingDevices = device->next; event->when = now; event->deviceId = device->id== mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id; event->type = DEVICE_REMOVED; event += 1; delete device; mNeedToSendFinishedDeviceScan =true; if (--capacity == 0) { break; } }//#若mClosingDevices不为空,那么将所有的mClosingDevices重置,并删除。
三.重新扫描设备。
在重新打开了设备,也删除了正在关闭的设备后,这里需要重新扫描设备。
if (mNeedToScanDevices) { mNeedToScanDevices = false; scanDevicesLocked(); mNeedToSendFinishedDeviceScan =true; }//#若mNeedToScanDevices判定为true,那么调用scanDevicesLocked voidEventHub::scanDevicesLocked() { status_t res = scanDirLocked(DEVICE_PATH); ………. if(mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) { createVirtualKeyboardLocked(); }}
这里扫描DEVICE_PATH,若扫描的结果中有VIRTUAL_KEYBOARD_ID那么就实例话一个虚拟键盘,这里的DEVICE_PATH是个"/dev/input",这个目录下有很多event,从event0àevent7,android版本的不同会有些出入。但是分别对应鼠标,触摸,按键事件之类。
1. 先看下createVirtualKeyboardLocked:
void EventHub::createVirtualKeyboardLocked() { InputDeviceIdentifier identifier; identifier.name = "Virtual"; identifier.uniqueId = ""; setDescriptor(identifier); Device* device = new Device(-1,VIRTUAL_KEYBOARD_ID, String8(""), identifier); device->classes = INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY | INPUT_DEVICE_CLASS_DPAD | INPUT_DEVICE_CLASS_VIRTUAL; loadKeyMapLocked(device); addDeviceLocked(device);} void EventHub::addDeviceLocked(Device*device) { mDevices.add(device->id, device); device->next = mOpeningDevices; mOpeningDevices = device;}
可以看到这里实例化了一个Device,并添加到mDevices中,重新创建索引后,将device赋值给mOpeningDevices,作为新的正在打开设备。
2. 我们再回到开始,来看下scanDirLocked
status_t EventHub::scanDirLocked(const char*dirname){ char devname[PATH_MAX]; char *filename; DIR *dir; struct dirent *de; dir = opendir(dirname); if(dir == NULL) return -1; strcpy(devname, dirname); filename = devname + strlen(devname); *filename++ = '/'; while((de = readdir(dir))) { if(de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue; strcpy(filename, de->d_name); openDeviceLocked(devname);//#------------------------mark } closedir(dir); return 0;}
这里传入的是/dev/input。那在这个目录下,有什么呢?
我在登录adb shell后,发现/dev/input中有event0-event7。其中,按键是event2,触摸是event5。这些设备文件会被分别打开。
status_t EventHub::openDeviceLocked(constchar *devicePath) { char buffer[80]; int fd = open(devicePath, O_RDWR | O_CLOEXEC);//#用这个传入的devicePath打开文件 if(fd < 0) { ALOGE("could not open %s, %s\n",devicePath, strerror(errno)); return -1; } InputDeviceIdentifier identifier; // Get device name. if(ioctl(fd, EVIOCGNAME(sizeof(buffer) - 1),&buffer) < 1) { //fprintf(stderr, "could not get device name for %s, %s\n",devicePath, strerror(errno)); }else { buffer[sizeof(buffer) - 1] = '\0'; identifier.name.setTo(buffer);}
//#控制device的io通道,这里的EVIOCGNAME是个_IOC_READ读信息的命令。如果打开io通道失败,那么,自己初始化buffer,设置成identifier.name的属性。
// Check to see if the device is on our excluded list for (size_t i = 0; i < mExcludedDevices.size(); i++) { const String8& item = mExcludedDevices.itemAt(i); if (identifier.name == item) { close(fd); return -1; }}
//#检查该device是否在我们需要排除的列表中。
// Get device driver version. int driverVersion; if(ioctl(fd, EVIOCGVERSION, &driverVersion)) { ALOGE("could not get driver version for %s, %s\n", devicePath,strerror(errno)); close(fd); return -1;}
//#通过ioctl命令,获取device的版本 // Get device identifier. struct input_id inputId; if(ioctl(fd, EVIOCGID, &inputId)) { ALOGE("could not get device input id for %s, %s\n",devicePath, strerror(errno)); close(fd); return -1; } identifier.bus = inputId.bustype; identifier.product = inputId.product; identifier.vendor = inputId.vendor;identifier.version= inputId.version;//#继续通过ioctl命令初始化 // Get device physical location. if(ioctl(fd, EVIOCGPHYS(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get location for %s, %s\n",devicePath, strerror(errno)); }else { buffer[sizeof(buffer) - 1] = '\0'; identifier.location.setTo(buffer);}//#继续通过ioctl命令初始化 // Get device unique id. if(ioctl(fd, EVIOCGUNIQ(sizeof(buffer) - 1), &buffer) < 1) { //fprintf(stderr, "could not get idstring for %s, %s\n",devicePath, strerror(errno)); }else { buffer[sizeof(buffer) - 1] = '\0'; identifier.uniqueId.setTo(buffer);}//#继续通过ioctl命令初始化 // Fill in the descriptor. setDescriptor(identifier);//#继续通过ioctl命令初始化,至此identifier的初始化就完成了 // Make file descriptor non-blocking for use with poll(). if (fcntl(fd, F_SETFL, O_NONBLOCK)) { ALOGE("Error %d making device file descriptor non-blocking.",errno); close(fd); return -1;} //#IO操作包括两部分:发出请求、结果完成。如果从发出请求到结果返回,一直block,那么就是blocking io;如果发出请求后,就可以返回,不考虑结果完成,那就是non-blocking io;那么,如果发出请求就返回,结果返回是block在select或者poll的,则称为 io multiplexing;如果发出请求就返回,结果返回通过call back方式处理,那么就是aio。 这里的nonblocking就是轮询,也就是说程序会定期的访问,直到内核缓冲区有数据为止。 // Allocate device. (The deviceobject takes ownership of the fd at this point.) int32_t deviceId = mNextDeviceId++; Device* device = new Device(fd, deviceId, String8(devicePath),identifier); // Load the configuration file for the device. loadConfigurationLocked(device); // Figure out the kinds of events the device reports. ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)),device->keyBitmask); ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)),device->absBitmask); ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)),device->relBitmask); ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)),device->swBitmask); ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)),device->ledBitmask); ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)),device->ffBitmask); ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)),device->propBitmask); // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for themost part. bool haveKeyboardKeys =containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) || containsNonZeroByte(device->keyBitmask,sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX +1)); bool haveGamepadButtons =containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), sizeof_bit_array(BTN_MOUSE)) || containsNonZeroByte(device->keyBitmask,sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)); if (haveKeyboardKeys || haveGamepadButtons){ device->classes |= INPUT_DEVICE_CLASS_KEYBOARD;}//#判断是否为键盘,这里用结构体device的classes成员变量或上 INPUT_DEVICE_CLASS_KEYBOARD,就标记为keyboard了 // See if this is a cursor device such as a trackball or mouse. if (test_bit(BTN_MOUSE, device->keyBitmask) && test_bit(REL_X, device->relBitmask) && test_bit(REL_Y, device->relBitmask)) { device->classes |= INPUT_DEVICE_CLASS_CURSOR;}//#判断是否为滚轮或鼠标,这里用结构体device的classes成员变量或上 INPUT_DEVICE_CLASS_CURSOR,就标记为滚轮或鼠标了 // See if this is a touch pad. // Is this a new modern multi-touch driver? if (test_bit(ABS_MT_POSITION_X, device->absBitmask) && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { device->classes |=INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; } // Is this an old style single-touch driver? }else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) && test_bit(ABS_Y, device->absBitmask)) { device->classes |=INPUT_DEVICE_CLASS_TOUCH;}//#判断是否为触摸板,这里用结构体device的classes成员变量或上 INPUT_DEVICE_CLASS_ TOUCH或者INPUT_DEVICE_CLASS_TOUCH_MT,就标记为滚轮或鼠标了 …….. if (haveGamepadButtons) { uint32_t assumedClasses =device->classes | INPUT_DEVICE_CLASS_JOYSTICK; for (int i = 0; i <= ABS_MAX; i++) { if (test_bit(i,device->absBitmask) &&(getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) { device->classes = assumedClasses; break; } }}//#游戏手柄?比如手柄。好神奇。居然这个也支持 // Check whether this device has switches. for (int i = 0; i <= SW_MAX; i++) { if (test_bit(i, device->swBitmask)) { device->classes |= INPUT_DEVICE_CLASS_SWITCH; break; }}//#开关? // Check whether this device supports the vibrator. if (test_bit(FF_RUMBLE, device->ffBitmask)) { device->classes |= INPUT_DEVICE_CLASS_VIBRATOR;} //#震动 // Configure virtual keys. if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { status_t status = loadVirtualKeyMapLocked(device); if (!status) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; }} //#虚拟键盘 // Load the key map. // We need to do this for joysticks too because the key layout mayspecify axes. status_t keyMapStatus = NAME_NOT_FOUND; if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD |INPUT_DEVICE_CLASS_JOYSTICK)) { // Load the keymap for the device. keyMapStatus = loadKeyMapLocked(device);}//#根据不同的配置需要loadKeyMap // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus && mBuiltInKeyboardId== NO_BUILT_IN_KEYBOARD &&isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; } //#将虚拟键盘,游戏手柄之类设备的id,赋值给mBuiltInKeyboardId // 'Q' key support = cheap test of whether this is an alpha-capable kbd if (hasKeycodeLocked(device, AKEYCODE_Q)) { device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; } // See if this device has a DPAD. if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && hasKeycodeLocked(device,AKEYCODE_DPAD_DOWN) && hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT)&& hasKeycodeLocked(device,AKEYCODE_DPAD_RIGHT) && hasKeycodeLocked(device,AKEYCODE_DPAD_CENTER)) { device->classes |= INPUT_DEVICE_CLASS_DPAD; } //#DPAD就是上下左右中五个键,以前在HTC的G2 还是什么机子上看到过。老机子了 // See if this device has a gamepad. for (size_t i = 0; i classes |= INPUT_DEVICE_CLASS_GAMEPAD; break; } } //#这里是游戏手柄 // Disable kernel key repeat since we handle it ourselves unsigned int repeatRate[] = {0,0}; if (ioctl(fd, EVIOCSREP, repeatRate)) { ALOGW("Unable to disable kernel key repeat for %s: %s",devicePath, strerror(errno)); } //#kernel会对按键长按做处理,而按键长按就会产生key repeat。如果没有repeat,也就没有长按,但是这里短按依旧是存在的。 } // If the device isn't recognized as something we handle, don't monitorit. if (device->classes == 0) { ALOGV("Dropping device: id=%d, path='%s', name='%s'", deviceId, devicePath,device->identifier.name.string()); delete device; return -1; } // Determine whether the device is external or internal. if (isExternalDeviceLocked(device)) { device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; } if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK |INPUT_DEVICE_CLASS_GAMEPAD)) { device->controllerNumber = getNextControllerNumberLocked(device); } // Register with epoll. struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.u32 = deviceId; if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd,&eventItem)) { ALOGE("Could not add device fd to epoll instance. errno=%d", errno); delete device; return -1;}//#通过epoll机制注册这个device,这里的EpollFd是EventHub结构体在被实例化时,所创建赋值的。Fd是当前的device的路径//# int fd =open(devicePath, O_RDWR | O_CLOEXEC);这句在之前可以找到 ……….. int clockId = CLOCK_MONOTONIC; bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId);………… addDeviceLocked(device); return 0;}
到这里,系统中的输入设备文件就打开了.
小结一下,重新扫描设备。这里,
1. 扫描设备/dev/input,如果有读到文件,就以此打开设备。
2. 在打开设备的过程中,系统要获得到设备的name,判断其实不是在被排除设备列表中,如果在被排除设备列表中,则直接返回。
3. 获取设备的版本,初始化设备的识别信息。
4. 这里设备的文件描述符被设置为non-blocking方式进行访问的,就是轮询,程序会定期的访问,知道内核缓冲区有数据为止。
5. 这个文件描述符、deviceid,devicepath、identifier作为信息传递给device结构体。通过device结构体的class变量,判断设备类型。将对应的信息储存到device结构体的class中。
6. 如果是键盘或者手柄,那就读取keymap。
7. 如果是键盘,那么就设置成内置的键盘。
8. 在打开设备后,释放kernel对长按的处理。只接受短按。
9. 最后将设备在epoll机制上注册。
10. 若设备列表中包含虚拟键盘,那就创建虚拟键盘。
四 打开 在mOpeningDevices 中的设备
如果mOpeningDevices不为空,那么就将其中的设备依次打开。
检测是否有新的设备进来。
while (mOpeningDevices != NULL) { Device* device = mOpeningDevices; ALOGV("Reporting deviceopened: 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 fd, readBuffer, sizeof(structinput_event) * capacity); if (readSize == 0 || (readSize< 0 && errno == ENODEV)) { // Device was removedbefore INotify noticed. deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { if (errno != EAGAIN&& errno != EINTR) { ALOGW("could notget event (errno=%d)", errno); } } else if ((readSize %sizeof(struct input_event)) != 0) { ALOGE("could not getevent (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 path.string(), (int)iev.time.tv_sec, (int) iev.time.tv_usec, iev.type,iev.code, iev.value); // Some input devices may have abetter concept of the time // when an input eventwas actually generated than the kernel // which simplytimestamps all events on entry to evdev. // This is a custom Android extension ofthe input protocol // mainly intended foruse with uinput based device drivers. if (iev.type == EV_MSC){ if (iev.code ==MSC_ANDROID_TIME_SEC) { device->timestampOverrideSec = iev.value; continue; } else if (iev.code== MSC_ANDROID_TIME_USEC) { device->timestampOverrideUsec = iev.value; continue; } } if(device->timestampOverrideSec || device->timestampOverrideUsec) { iev.time.tv_sec =device->timestampOverrideSec; iev.time.tv_usec =device->timestampOverrideUsec; if (iev.type ==EV_SYN && iev.code == SYN_REPORT) { device->timestampOverrideSec = 0; device->timestampOverrideUsec = 0; } ALOGV("appliedoverride time %d.%06d", int(iev.time.tv_sec), int(iev.time.tv_usec)); } #ifdefHAVE_POSIX_CLOCKS event->when =nsecs_t(iev.time.tv_sec) * 1000000000LL +nsecs_t(iev.time.tv_usec) * 1000LL; if (event->when >= now + 10 *1000000000LL) { //Double-check. Time may have moved on. nsecs_t time =systemTime(SYSTEM_TIME_MONOTONIC); if (event->when> time) { event->when =time; } else { ………. } }#else event->when = now;#endif event->deviceId =deviceId; event->type =iev.type; event->code =iev.code; event->value =iev.value; event += 1; capacity -= 1; } if (capacity == 0) { // The result buffer isfull. Reset the pending event index // so we will try toread the device again on the next iteration. mPendingEventIndex -= 1; break; } } } else if (eventItem.events &EPOLLHUP) { ALOGI("Removing device %sdue to epoll hang-up event.", device->identifier.name.string()); deviceChanged = true; closeDeviceLocked(device); } else { ALOGW("Received unexpectedepoll event 0x%08x for device %s.", eventItem.events,device->identifier.name.string()); } }
//#根据设备索引获取设备文件句柄后,通过device结构体的fd读取input_event事件,事件的个数保存在capacity,
readSize是read出来的结果,readSize除以 sizeof(struct input_event)得到实际读取的事件个数。
读掉一个event,capacity就-1,直到0就退出循环,表明buffer已经读满了,那就是读了256个。
如果不等于0,就回到while循环判断mPendingEventIndex和 mPendingEventCount的关系,
如果判断成立,就从mPendingEventItems取下一个事件,如果相等,就表示事件已经取完了
七.读取INofity信息
// readNotify() will modify the list of devices so this must be doneafter // processing all other events to ensure that we read all remainingevents // before closing the devices. if (mPendingINotify && mPendingEventIndex >=mPendingEventCount) { mPendingINotify = false; readNotifyLocked(); deviceChanged = true; }// Report added or removed devices immediately. if (deviceChanged) { continue; } 如果mPendingINotify为true,同时mPendingEventIndex>=mPendingEventCount,就说明发生了热插拔,调用readNotifyLocked(); 这里如果deviceChanged,就开始下一次循环,即立即执行设备的添加或删除。 status_tEventHub::readNotifyLocked() { ………. res = read(mINotifyFd, event_buf,sizeof(event_buf)); …… strcpy(devname, DEVICE_PATH); filename = devname + strlen(devname); *filename++ = '/'; while(res >= (int)sizeof(*event)) { event = (struct inotify_event*)(event_buf + event_pos); if(event->len) { strcpy(filename, event->name); if(event->mask & IN_CREATE){ openDeviceLocked(devname); } else { closeDeviceByPathLocked(devname); } } ………………. } return 0;}
readNotifyLocked从event_buf中读取出inotify_event事件,获取设备名,设备节点,
判断event->mask,如果是IN_CREATE那么就调用openDeviceLocked,添加设备,
否则,那么就是IN_DELETE,将设备删除。
八.事件处理完成,重新进入循环等待中
……………int pollResult = epoll_wait(mEpollFd,mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);……………if (pollResult == 0) { // Timed out. mPendingEventCount = 0; break; } if (pollResult < 0) { …… } else { // Some events occurred. mPendingEventCount =size_t(pollResult); }
这里表明epoll_event会被保存在mPendingEventItems中,事件数量会被保存在mPendingEventCount中。这里只保存数量,内容依旧是需要通过read去读取的。容需要通过read来读取。
// All done, return the number of events we read.
return event - buffer;
返回得到的event个数,为下一次读取做准备。
至此,getEvent分析完毕。。。。。。。。。。。。InputReader方法的loopOnce也就完成了。
InputRead事件的读取也就分析完了。
事件分发
InputManager从SystemServer中启动后就一直监控键盘输入事件。换言之,就是全程监控。而键盘事件的分发是分发给当前激活的activity窗口的
大概的流程就是:ActivityThread:createBaseContextForActivityàContextImpl:createActivityContextànew ContextImplàContextImpl:registerServer(WINDOW_SERVICE)à newWindowManagerImplà WindowManagerGlobal:addViewàViewRootImpl:setView
这个按键的处理流程,从ViewRootImpl:setView开始说
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView){
synchronized (this) {
if (mView == null) {
………
mFallbackEventHandler.setView(view);//#不在此次讨论的范围,但也做了些事情,比如说AudioManager中对音量增减的调用
…………..
// Schedule the first layout-before- adding to the window
// manager, to make sure we dothe relayout before receiving
// any other events from the system.
requestLayout();
if((mWindowAttributes.inputFeatures &WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel= new InputChannel();
}
try {
…….
res= mWindowSession.addToDisplay(mWindow, mSeq,
mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
} catch (RemoteException e) {
………
} finally {
………….
}
…………….
if (view instanceofRootViewSurfaceTaker) {
mInputQueueCallback =
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback !=null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = newWindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
………….
// Set up the input pipeline.
CharSequence counterSuffix =attrs.getTitle();
InputStage syntheticInputStage= new SyntheticInputStage();
InputStage viewPostImeStage =new ViewPostImeInputStage(syntheticInputStage);
InputStage nativePostImeStage =new NativePostImeInputStage(viewPostImeStage,
"aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage =new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = newImeInputStage(earlyPostImeStage,
"aq:ime:" + counterSuffix);
InputStage viewPreImeStage =new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage =new NativePreImeInputStage(viewPreImeStage,
"aq:native-pre-ime:" + counterSuffix);
mFirstInputStage =nativePreImeStage;
mFirstPostImeInputStage =earlyPostImeStage;
mPendingInputEventQueueLengthCounterName = "aq:pending:" +counterSuffix;
}
}
}
这里有几个函数需要注意。
requestLayout
在一般情况下,我们说这个函数会获取当前activity的焦点。这里会调用到relayoutWindow,再通过sWindowSession:relayout,IWindowSession使用其成员变量mRemote所描述的Binder代理对象向运行在WindowManagerService服务内部的的Session对象发送TRANSACTION_relayout。窗口的信息就包含在这个Binder对象中。
大概的描述下过程:
requestLayoutàscheduleTraversalsàmTraversalRunnableàTraversalRunnable:doTraversalàperformTraversalsàrelayoutWindowàmWindowSession.relayout
Session.relayout
函数定位:frameworks/base/services/java/com/android/server/wm/Session.java
public int relayout(IWindow window, int seq, WindowManager.LayoutParamsattrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { ……… int res = mService.relayoutWindow(this, window, seq, attrs, requestedWidth,requestedHeight, viewFlags, flags, outFrame, outOverscanInsets, outContentInsets,outVisibleInsets, outConfig, outSurface); ………. return res;}
继而有调用了mService的成员函数relayoutWindow。
这里的mService是WindowManagerService。
WindowManagerService.relayoutWIndow
函数定位:
frameworks/base/services/java/com/android/server/wm/WindowManagerService.java
public intrelayoutWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs,int requestedWidth, int requestedHeight, intviewVisibility, int flags, Rect outFrame, RectoutOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,Configuration outConfig, Surface outSurface) { ……………. mInputMonitor.updateInputWindowsLw(true/*force*/); …………….}
InputMonitor:updateInputWindowsLw
函数定位:frameworks/base/services/java/com/android/server/wm/InputMonitor.java
public void updateInputWindowsLw(boolean force) ……….. // Add allwindows on the default display. final intnumDisplays = mService.mDisplayContents.size(); for (intdisplayNdx = 0; displayNdx < numDisplays; ++displayNdx) { WindowListwindows = mService.mDisplayContents.valueAt(displayNdx).getWindowList(); for (intwinNdx = windows.size() - 1; winNdx >= 0; --winNdx) { finalWindowState child = windows.get(winNdx); finalInputChannel inputChannel = child.mInputChannel; final InputWindowHandle inputWindowHandle =child.mInputWindowHandle; if(inputChannel == null || inputWindowHandle == null || child.mRemoved) { // Skip this window because it cannot possiblyreceive input. continue; } ……… } } // Send windowsto native code. mService.mInputManager.setInputWindows(mInputWindowHandles); ……….. }
这里将mInputWindowHandles,从字面上看是InputManagerService的句柄,可以理解成如果InputManagerService不为空,就设置为InputManager的输入窗口。
InputManagerService:setInputWindows
函数定位:frameworks/base/services/java/com/android/server/input / InputManagerService.java
public void setInputWindows(InputWindowHandle[]windowHandles) { nativeSetInputWindows(mPtr, windowHandles);}
com_android_server_input_InputManagerService.cpp:nativeSetInputWindows
定位:frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
static void nativeSetInputWindows(JNIEnv*env, jclass clazz, jint ptr, jobjectArraywindowHandleObjArray) { NativeInputManager* im = reinterpret_cast(ptr); im->setInputWindows(env,windowHandleObjArray);}
com_android_server_input_InputManagerService.cpp:setInputWindows
定位:frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
void NativeInputManager::setInputWindows(JNIEnv* env,jobjectArray windowHandleObjArray) {
Vector
if(windowHandleObjArray) {
jsize length =env->GetArrayLength(windowHandleObjArray);
for (jsize i =0; i < length; i++) {
jobjectwindowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
if (!windowHandleObj) {
break;// found null element indicating end of used portion of the array
}
sp
android_server_InputWindowHandle_getHandle(env, windowHandleObj);
if(windowHandle != NULL) {
windowHandles.push(windowHandle);
}
env->DeleteLocalRef(windowHandleObj);
}
}
mInputManager->getDispatcher()->setInputWindows(windowHandles);
…….
if (changes) {
mInputManager->getReader()->requestRefreshConfiguration(changes);
}
}
这里将java层的传入的windowHandleObjArray转化成InputWindowHandle。然后压入windowHandles。然后,调用InputDispatcher的setInputWindows,将传入的句柄传入InputDispatcher中。
InputDispatcher.cpp: setInputWindows
定位:frameworks/base/services/input/InputDispatcher.cpp
void InputDispatcher::setInputWindows(constVector >& inputWindowHandles) { { // acquire lock AutoMutex_l(mLock); Vector > oldWindowHandles =mWindowHandles; mWindowHandles= inputWindowHandles; sp newFocusedWindowHandle; boolfoundHoveredWindow = false; for (size_t i =0; i < mWindowHandles.size(); i++) { constsp& 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) { if(mFocusedWindowHandle != NULL) { sp focusedInputChannel =mFocusedWindowHandle->getInputChannel(); if(focusedInputChannel != NULL) { CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, "focus left window"); synthesizeCancelationEventsForInputChannelLocked( focusedInputChannel, options); } } mFocusedWindowHandle= newFocusedWindowHandle; } for (size_t i =0; i < mTouchState.windows.size(); i++) { TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i); if(!hasWindowHandleLocked(touchedWindow.windowHandle)) { sp touchedInputChannel = touchedWindow.windowHandle->getInputChannel(); if(touchedInputChannel != NULL) { CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked( touchedInputChannel, options); } mTouchState.windows.removeAt(i--); } }………… } // release lock………..}
更新当前的focused的InputWindowHandle。并将mTouchState.windows与同步mWindowHandles同步。
InputDispatcher:synthesizeCancelationEventsForInputChannelLocked
void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( constsp& channel, const CancelationOptions& options) { ssize_t index =getConnectionIndexLocked(channel); if (index >= 0){ synthesizeCancelationEventsForConnectionLocked( mConnectionsByFd.valueAt(index), options); }}
直接到下一步调用
InputDispatcher: synthesizeCancelationEventsForConnectionLocked
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
constsp
……….
if(!cancelationEvents.isEmpty()) {
for (size_t i =0; i < cancelationEvents.size(); i++) {
…….
enqueueDispatchEntryLocked(connection, cancelationEventEntry, // incrementsref&target, InputTarget::FLAG_DISPATCH_AS_IS);
……..
}
startDispatchCycleLocked(currentTime, connection);
}
}
InputDispatcher:enqueueDispatchEntryLocked
void InputDispatcher::enqueueDispatchEntryLocked(
constsp
……
// This is a newevent.
// Enqueue a newdispatch entry onto the outbound queue for this connection.
DispatchEntry* dispatchEntry = newDispatchEntry(eventEntry, // increments ref
inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
inputTarget->scaleFactor);
// Apply targetflags and update the connection's input state.
switch(eventEntry->type) {
caseEventEntry::TYPE_KEY: {
KeyEntry*keyEntry = static_cast
dispatchEntry->resolvedAction= keyEntry->action;
dispatchEntry->resolvedFlags = keyEntry->flags;
if(!connection->inputState.trackKey(keyEntry,
dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
deletedispatchEntry;
return; //skip the inconsistent event
}
break;
}
……..
}
………….
// Enqueue thedispatch entry.
connection->outboundQueue.enqueueAtTail(dispatchEntry);
……….
}
这里InputTarget的相关属性传入DispatchEntry的结构体,调用Connection的成员变量inputState的trackKey方法过滤已处理的按键。过滤后的DispatchEntry添加到Connection的成员变量outboundQueue的尾部。
接着看 startDispatchCycleLocked
void InputDispatcher::startDispatchCycleLocked(nsecs_tcurrentTime, constsp& connection) { while(connection->status == Connection::STATUS_NORMAL &&!connection->outboundQueue.isEmpty()) { DispatchEntry*dispatchEntry = connection->outboundQueue.head; dispatchEntry->deliveryTime = currentTime; // Publish theevent. status_tstatus; EventEntry*eventEntry = dispatchEntry->eventEntry; switch(eventEntry->type) { caseEventEntry::TYPE_KEY: { KeyEntry*keyEntry = static_cast(eventEntry); // Publishthe key event. status =connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, keyEntry->deviceId, keyEntry->source, dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, keyEntry->keyCode,keyEntry->scanCode, keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, keyEntry->eventTime); break; } ……….. default: return; } // Check theresult. if (status) { if (status== WOULD_BLOCK) { if(connection->waitQueue.isEmpty()) { abortBrokenDispatchCycleLocked(currentTime, connection, true/*notify*/); } else { //Pipe is full and we are waiting for the app to finish process some events connection->inputPublisherBlocked = true; } } else { abortBrokenDispatchCycleLocked(currentTime, connection, true/*notify*/); } return; } // Re-enqueuethe event on the wait queue. connection->outboundQueue.dequeue(dispatchEntry); traceOutboundQueueLengthLocked(connection); connection->waitQueue.enqueueAtTail(dispatchEntry); traceWaitQueueLengthLocked(connection); }}
这里将事件从队列中取出后,将其封装到connection->inputPublisher.publishKeyEvent中去派发出去。若pipe已满,则将状态更改成阻塞。若事件状态正常,那么直接到最后的话,就是事件没有被处理,则重新添加到队列尾部再处理一次。
这样,requestLayout就讲完了。这样requestLayout将事件分发给当前激活的activity处理。
继续回到事件派发。
mWindowSession.addToDisplay
在之前的代码中可以看到
res =mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets,mInputChannel);
mWindowSession会连接到WindowManagerService。通过binder的方式进行进程间的通信。
Session:addToDisplay public intaddToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, intviewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { returnmService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outContentInsets, outInputChannel); }
WindowManagerService:addWindow
public intaddWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
RectoutContentInsets, InputChannel outInputChannel) {
……..
synchronized(mWindowMap) {
……..
win = newWindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
……..
if(outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
mInputManager.registerInputChannel(win.mInputChannel,win.mInputWindowHandle);
}
……..
win.attach();
mWindowMap.put(client.asBinder(), win);
……..
mInputMonitor.setUpdateInputWindowsNeededLw();
….
mInputMonitor.updateInputWindowsLw(false /*force*/);
…….
}
…….
return res;
}
通过window创建InputChannel,通过InputManager注册InputChannel,注册后,产生一个接受端,处理后续产生的事件。这个Window会保存在WindowManagerServer的成员变量mWindowMap中。
看一下InputChannel:openInputChannelPair
定位:frameworks/base/core/java/android/view/InputChannel.java
public staticInputChannel[] openInputChannelPair(String name) { if (name ==null) { throw newIllegalArgumentException("name must not be null"); } returnnativeOpenInputChannelPair(name);}
android_view_InputChannel.cpp:nativeOpenInputChannelPair
定位:frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectArrayandroid_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz,jstring nameObj) {
const char*nameChars = env->GetStringUTFChars(nameObj, NULL);
String8name(nameChars);
env->ReleaseStringUTFChars(nameObj,nameChars);
sp
sp
status_t result = InputChannel::openInputChannelPair(name,serverChannel, clientChannel);
if (result) {
String8message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
jobjectArraychannelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if(env->ExceptionCheck()) {
return NULL;
}
jobjectserverChannelObj =android_view_InputChannel_createInputChannel(env,
newNativeInputChannel(serverChannel));
if(env->ExceptionCheck()) {
return NULL;
}
jobjectclientChannelObj = android_view_InputChannel_createInputChannel(env,
newNativeInputChannel(clientChannel));
if(env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair,0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
InputChannel:openInputChannelPair
定位:frameworks/native/libs/input/InputTransport.cpp
status_t InputChannel::openInputChannelPair(constString8& name, sp& outServerChannel,sp& outClientChannel) { int sockets[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { status_t result = -errno; outServerChannel.clear(); outClientChannel.clear(); return result; } 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 = newInputChannel(serverChannelName, sockets[0]); String8 clientChannelName = name; clientChannelName.append(" (client)"); outClientChannel = newInputChannel(clientChannelName, sockets[1]); return OK;}
不得不说以前是用pipe。现在改成socket了。
两者都能处理字节流。但是处理方法不一样。
Pipe:只存在一个指定主机内,适用于在这个指定主机内进行虚拟文件间的缓存或者连接这个主内内输入/输出进程。同时,pipe没有包的概念
Socket:用IPv4或者IPv6通信。Socket的通信可以不局限于一个主机,可以跨主机,socket的通信可以用在本地通信,但是必须使用不同的端口。
http://stackoverflow.com/questions/18568089/whats-the-difference-between-pipes-and-sockets
android_view_InputChannel_createInputChannel
定位:frameworks/base/core/jni/android_view_InputChannel.cpp
static jobjectandroid_view_InputChannel_createInputChannel(JNIEnv* env, NativeInputChannel* nativeInputChannel) { jobjectinputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.ctor); if(inputChannelObj) { android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel); } returninputChannelObj;} static voidandroid_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobjectinputChannelObj, NativeInputChannel* nativeInputChannel) { env->SetIntField(inputChannelObj, gInputChannelClassInfo.mPtr, reinterpret_cast(nativeInputChannel));}
InputManagerService:registerInputChannel
public void registerInputChannel(InputChannel inputChannel, InputWindowHandleinputWindowHandle) { if (inputChannel == null) { throw newIllegalArgumentException("inputChannel must not be null."); } nativeRegisterInputChannel(mPtr,inputChannel, inputWindowHandle, false); }
com_android_server_input_InputManagerService.cpp:nativeRegisterInputChannel
定位:frameworks/base/services/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclassclazz, jint ptr,jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) { NativeInputManager*im = reinterpret_cast(ptr); sp inputChannel =android_view_InputChannel_getInputChannel(env, inputChannelObj); if (inputChannel ==NULL) { throwInputChannelNotInitialized(env); return; } sp inputWindowHandle = android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); status_tstatus = im->registerInputChannel( env,inputChannel, inputWindowHandle, monitor); if (status) { String8message; message.appendFormat("Failed to register input channel. status=%d", status); jniThrowRuntimeException(env, message.string()); return; } if (! monitor) { android_view_InputChannel_setDisposeCallback(env,inputChannelObj, handleInputChannelDisposed, im); }}
这里的imàregisterInputChannel执行了真正的注册。
NativeInputManager:registerInputChannel
status_t NativeInputManager::registerInputChannel(JNIEnv* env, const sp& inputChannel, const sp& inputWindowHandle, bool monitor){ return mInputManager->getDispatcher()->registerInputChannel( inputChannel, inputWindowHandle, monitor);}
还是调用了InputDispatcher的registerInputChannel。
InputDispater:registerInputChannel
status_t InputDispatcher::registerInputChannel(const sp
const sp
{// acquire lock
AutoMutex _l(mLock);
if (getConnectionIndexLocked(inputChannel)>= 0) {
ALOGW("Attempted to register alreadyregistered input channel'%s'",
inputChannel->getName().string());
return BAD_VALUE;
}
sp
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd,0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
}// release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
registerInputChannel会先通过getConnectionIndexLocked检查被传进来的InputChannel是否已经被注册过了。被注册---》返回BAD_VALUE;如果没有被注册,就将参数传入Connection结构体。mConnectionByFd是KeyedVector的数组。inputChannelàgetFd()。得到InputChannel的文件描述符后,作为key保存在mConnectionByFd中,而实例化出来的connection则是value。
若需要监控,就将InputChannel压入mMonitoringChannels的栈中。
最后从InputChannel得到的文件描述符fd作为参数添加到InputDispater的成员变量mLooper中。换言之,这里用的也是pipe机制。
继续看ViewRootImpl. setView
if (view instanceof RootViewSurfaceTaker) {
mInputQueueCallback=
((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
}
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue= new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver= new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
先看下view是否是RootViewSurfaceTaker,实际情况下,DecorView是一个 FrameLayout并实现了RootViewSurfaceTaker,而其他的view一般都不是RootViewSurfaceTaker,DecorView才是RootViewSurfaceTaker。
整个aosp搜索下,也只有DecorView
private final class DecorView extends FrameLayout implementsRootViewSurfaceTaker
那么,如果就会执行到
mInputEventReceiver = newWindowInputEventReceiver(mInputChannel, Looper.myLooper());
这里的WindowInputEventReceiver继承自InputEventReceiver.java
定位:frameworks/base/core/java/android/view/InputEventReceiver.java
publicInputEventReceiver(InputChannel inputChannel, Looper looper) { ……… mInputChannel =inputChannel; mMessageQueue =looper.getQueue(); mReceiverPtr =nativeInit(new WeakReference(this), inputChannel, mMessageQueue); mCloseGuard.open("dispose"); }
InputEventReceiver在java层保存了一个MessageQueue。通过nativeInit获得一个mReceiverPtr,后续会使用这个句柄进行事件的分发。
nativeInit实现在android_view_InputEventReceiver
定位:frameworks/base/core/jni/android_backup_BackupDataInput.cpp
static jint nativeInit(JNIEnv* env, jclass clazz, jobjectreceiverWeak, jobjectinputChannelObj, jobject messageQueueObj) { …….. sp receiver = newNativeInputEventReceiver(env, receiverWeak, inputChannel, messageQueue); status_t status =receiver->initialize(); if (status) { String8message; message.appendFormat("Failed to initialize input eventreceiver. status=%d", status); jniThrowRuntimeException(env, message.string()); return 0; } receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain areference for the object returnreinterpret_cast(receiver.get());} NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv*env, jobjectreceiverWeak, const sp& inputChannel, constsp& messageQueue) : mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mInputConsumer(inputChannel), mMessageQueue(messageQueue), mBatchedInputEventPending(false), mFdEvents(0) {}
这里只是将将参数保存到native层。
打断:
小结,这里就完成了按键事件的接受~~~~~~~~~~
1. 上层的activity被激活后,会通过RootViewImpl.setView通知InputManager,一旦事件发生,InputManager就会把按键事件派发给这个activity处理
2. Activity与InputManager之间通过InputChannel通信。这里的通信由Server以及Client端构成。Server端会在InputDispatcher被管理,而Client端在应用程序的mLooper中。
3. 通信的根本是基于pipe机制。
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t*nextWakeupTime) {
nsecs_t currentTime = now();
…………….
// Ready to start a new event.
// If we don't already have a pending event, go grab one.
if (! mPendingEvent) {
if (mInboundQueue.isEmpty()) {
……………..
// Nothing to do if there is no pending event.
if (!mPendingEvent) {
return;
}
//#这部分是说 如果mInboundQueue的等待队列是空的。就是没有消息。那么,简单设置下状态什么的,退出
} else {
// Inbound queue has at leastone entry.
mPendingEvent = mInboundQueue.dequeueAtHead();
traceInboundQueueLengthLocked();
}
// Poke user activity for this event.
if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(mPendingEvent);
}
…………..
}
// Now we have an event to dispatch.
// All events are eventually dequeued and processed this way, even if weintend to drop them.
…..............
switch (mPendingEvent->type) {
case EventEntry::TYPE_CONFIGURATION_CHANGED: {
………….
}
case EventEntry::TYPE_DEVICE_RESET: {
………….
}
case EventEntry::TYPE_KEY: {
KeyEntry* typedEntry = static_cast
………….//这部分是dropReason的判定。无关。就……
done = dispatchKeyLocked(currentTime, typedEntry, &dropReason,nextWakeupTime);
break;
}
case EventEntry::TYPE_MOTION: {
…………
}
default:
ALOG_ASSERT(false);
break;
}
……………
}
关注红色部分,如果mPendingEvent为空,这时,若mInboundQueue有数据,那么就取出mInboundQueue.dequeueAtHead();并赋值给mPendingEvent;否则就简单的重置下状态退出。
如果mPendingEvent不为Null,根据mPendingEvent->type的类型,根据类型的不同进行操作,如果类型是EventEntry::TYPE_KEY,那就将mPendingEvent 封装成KeyEntry,并将封装好的mPendingEvent作为参数,传递给dispatchKeyLocked(currentTime, typedEntry, &dropReason,nextWakeupTime);
那么继续看dispatchKeyLocked函数
bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { // Preprocessing. ………………//# // Handle case where the policy asked us to try again later last time. ……………………. // Give the policy a chance to intercept the key. ………….. // Clean up if dropping the event. …………. // Identify targets. Vector inputTargets; int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, entry, inputTargets, nextWakeupTime); ………… addMonitoringTargetsLocked(inputTargets); // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true;}
findFocusedWindowTargetsLocked:找到当前有焦点的window,这个window就是当前激活的activity。
dispatchEventLocked:找到之前有焦点的window后,在用dispatchEventLocked分发出去
.
先看findFocusedWindowTargetsLocked是怎么实现的?
int32_tInputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,Vector& inputTargets, nsecs_t* nextWakeupTime) { int32_t injectionResult; // If there is no currently focused windowand no focused application // then drop the event. if (mFocusedWindowHandle == NULL) { if (mFocusedApplicationHandle != NULL){ injectionResult =handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplicationHandle,NULL, nextWakeupTime, "Waiting because nowindow has focus but there is a " "focused applicationthat may eventually add a window " "when it finishesstarting up."); goto Unresponsive; } ALOGI("Dropping event becausethere is no focused window or focused application."); injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } // Check permissions. if (!checkInjectionPermission(mFocusedWindowHandle, entry->injectionState)) { injectionResult =INPUT_EVENT_INJECTION_PERMISSION_DENIED; goto Failed; } // If the currently focused window ispaused then keep waiting. if(mFocusedWindowHandle->getInfo()->paused) { injectionResult =handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplicationHandle,mFocusedWindowHandle, nextWakeupTime, "Waiting because thefocused window is paused."); goto Unresponsive; } // If the currently focused window is stillworking on previous events then keep waiting. if(!isWindowReadyForMoreInputLocked(currentTime, mFocusedWindowHandle, entry)) { injectionResult =handleTargetsNotReadyLocked(currentTime, entry, mFocusedApplicationHandle,mFocusedWindowHandle, nextWakeupTime, "Waiting because the focused window hasnot finished " "processing the inputevents that were previously delivered to it."); goto Unresponsive; } // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; addWindowTargetLocked(mFocusedWindowHandle, InputTarget::FLAG_FOREGROUND |InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), inputTargets); // Done.Failed:Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); updateDispatchStatisticsLocked(currentTime,entry, injectionResult,timeSpentWaitingForApplication);………. return injectionResult;}
这里分几个情况:
1. 如果mFocusedWindowHandle为null,那就是当前没有激活的activity,那就接着判断mFocusedApplicationHandle是否为null,如果有application的焦点,那就把event传递给它的application去处理。
2. 然后检查权限,按键事件可以由硬件跟软件触发。如果是硬件,那么entry->injectionState为null。如果是软件,那么entry->injectionState会带有注入的processid之类的信息。比如说我经常用的虚拟按键,那就是软件触发。
3. 如果当前有focus的window被pause,那么event就被halt,keep waiting
4. 检查是否当前被激活的window正在处理其他event,有则等待。
5. 最终调用addWindowTargetLocked处理当前的event
那么,继续看addWindowTargetLocked
void InputDispatcher::addWindowTargetLocked(const sp&windowHandle, int32_t targetFlags, BitSet32pointerIds, Vector& inputTargets) { inputTargets.push(); const InputWindowInfo* windowInfo =windowHandle->getInfo(); InputTarget& target =inputTargets.editTop(); target.inputChannel =windowInfo->inputChannel; target.flags = targetFlags; target.xOffset = -windowInfo->frameLeft; target.yOffset = - windowInfo->frameTop; target.scaleFactor =windowInfo->scaleFactor; target.pointerIds = pointerIds;}
这里将传入的inputTargets,压入栈中,并保存栈顶的InputTarget的属性。
继续看dispatchEventLocked是怎么实现的?
voidInputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* eventEntry, constVector& inputTargets) {………………… pokeUserActivityLocked(eventEntry); for (size_t i = 0; i = 0) { sp connection =mConnectionsByFd.valueAt(connectionIndex); prepareDispatchCycleLocked(currentTime,connection, eventEntry, &inputTarget); } else {……………… } }}
pokeUserActivityLocked:这里最终会调用掉powermanagerservice的userActivity,而唤醒activity。就是有按键进来的话,会传给powermanagerservice做处理
prepareDispatchCycleLocked:将获取到的事件分发出去。
更多相关文章
- android系统中“关于设备”中android版本和android安全补丁信息
- Android设备功能之录音教程篇
- Java事件模型与Android事件模型的比较
- Android调用输入法软键盘,返回输入的内容
- Android软键盘回车键修改为搜索按键
- 诡异特殊的EditText 弹出软键盘遮挡BUG
- Android菜鸟的成长笔记(11)——Android中的事件处理
- Android EditText进入页面不聚焦设置(进入页面不弹出键盘,点击弹出
- android edittext 隐藏键盘