扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
这篇文章主要讲解了“Android中如何利用Input子系统监听线程的启动”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android中如何利用Input子系统监听线程的启动”吧!
专注于为中小企业提供网站设计制作、成都网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业邯山免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
InputManagerService初始化概览
首先,有几点共识我们都可以达成:
Android Framework层的Service(Java)都是由system_server进程创建的(由于没有fork,因此都运行在system_server进程中)
Service创建后就会交给运行在system_server进程中的ServiceManager管理。
因此对于InputManagerService的创建,我们可以在SystemServer的startOtherServices()方法中找到,该方法做了以下事情:
创建InputManagerService对象
将它交给ServiceManager管理
将WindowManagerService的InputMonitor注册到InputManagerService中作为窗口响应事件后的回调
完成以上工作后启动InputManagerService。
SystemServer.javastartOtherServices(){ …… inputManager = new InputManagerService(context); …… inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); …… }
接下来我们就逐部分学习相应的处理。
InputManagerService对象的创建
创建InputManagerService对象时会完成以下工作:
创建一个负责处理DisplayThread线程中的Message的Handler
调用nativeInit初始化native层的InputManagerService,初始化的时候传入了DisplayThread的消息队列
用mPtr保存native层的InputManagerService
初始化完成后将Service添加到LocalServices,通过Map以键值对的形式存储
InputManagerService.javapublic InputManagerService(Context context) { this.mContext = context; this.mHandler = new InputManagerHandler(DisplayThread.get().getLooper()); mUseDevInputEventForAudioJack = context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack); Slog.i(TAG, "Initializing input manager, mUseDevInputEventForAudioJack=" + mUseDevInputEventForAudioJack); mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue()); LocalServices.addService(InputManagerInternal.class, new LocalService()); }
这里可能有人就会问了,为什么InputManagerService要和DisplayThread绑定在一起?大家不妨想想,InputEvent无论如何被获取、归类、分发,最终还是要被处理,也就意味着最终它的处理结果都要在UI上体现,那么InputManagerService自然要选择和UI亲近一些的线程在一起了。
但是问题又来了,应用都是运行在自己的主线程里的,难道InputManagerService要一个个绑定么,还是一个个轮询?这些做法都太过低效,那换个办法,可不可以和某个管理或非常亲近所有应用UI的线程绑定在一起呢?
答案是什么,我在这里先不说,大家可以利用自己的知识想想。
初始化native层的InputManagerService
在nativeInit函数中,将Java层的MessageQueue转换为native层的MessageQueue,然后再取出Looper用于NativeInputManager的初始化。可见这里的重头戏就是NativeInputManager的创建,这个过程做了以下事情:
将Java层的Context和InputManagerService转换为native层的Context和InputManagerService存储在mContextObj和mServiceObj中
初始化变量
创建EventHub
创建InputManager
com_android_server_input_InputManagerService.cpp NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp& looper) : mLooper(looper), mInteractive(true) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); mServiceObj = env->NewGlobalRef(serviceObj); { AutoMutex _l(mLock); mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; mLocked.pointerGesturesEnabled = true; mLocked.showTouches = false; } mInteractive = true; sp eventHub = new EventHub(); mInputManager = new InputManager(eventHub, this, this); }
EventHub
看到这里很多人就会想,EventHub是什么?取英语释义来看,它的意思是事件枢纽。我们在文章开头的时候也提到过,Input系统的事件来源于驱动/内核,那么我们可以猜测EventHub是处理来自驱动/内核的元事件的枢纽。接下来就在源码中验证我们的想法吧。
EventHub的创建过程中做了以下事情:
创建mEpollFd用于监听是否有数据(有无事件)可读
创建mINotifyFd将它注册到DEVICE_PATH(这里路径就是/dev/input)节点,并将它交给内核用于监听该设备节点的增删数据事件。那么只要有数据增删的事件到来,epoll_wait()就会返回,使得EventHub能收到来自系统的通知,并获取事件的详细信息
调用epoll_ctl函数将mEpollFd和mINotifyFd注册到epoll中
定义int wakeFd[2]作为事件传输管道的读写两端,并将读端注册到epoll中让mEpollFd监听
EventHub.cpp 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); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); mINotifyFd = inotify_init(); int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); …… 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); …… result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); …… }
那么这里抛出一个问题:为什么要把管道的读端注册到epoll中?假如EventHub因为getEvents读不到事件而阻塞在epoll_wait()里,而我们没有绑定读端的话,我们要怎么唤醒EventHub?如果绑定了管道的读端,我们就可以通过向管道的写端写数据从而让EventHub因为得到管道写端的数据而被唤醒。
InputManager的创建
接下来继续说InputManager的创建,它的创建就简单多了,创建一个InputDispatcher对象用于分发事件,一个InputReader对象用于读事件并把事件交给InputDispatcher分发,,然后调用initialize()初始化,其实也就是创建了InputReaderThread和InputDispatcherThread。
InputManager.cpp InputManager::InputManager( const sp& eventHub, const sp & readerPolicy, const sp & dispatcherPolicy) { mDispatcher = new InputDispatcher(dispatcherPolicy); mReader = new InputReader(eventHub, readerPolicy, mDispatcher); initialize(); }void InputManager::initialize() { mReaderThread = new InputReaderThread(mReader); mDispatcherThread = new InputDispatcherThread(mDispatcher); }
InputDispatcher和InputReader的创建都相对简单。InputDispatcher会创建自己线程的Looper,以及设置根据传入的dispatchPolicy设置分发规则。InputReader则会将传入的InputDispatcher封装为监听对象存起来,做一些数据初始化就结束了。
至此,InputManagerService对象的初始化就完成了,根据开头说的,接下来就会调用InputManagerService的start()方法。
监听线程InputReader和InputDispatcher的启动
在start()方法中,做了以下事情:
调用nativeStart方法,其实就是调用InputManager的start()方法
将InputManagerService交给WatchDog监控
注册触控点速度、显示触控的观察者,并注册广播监控它们
主动调用updateXXX方法更新(初始化)
InputManagerService.javapublic void start() { Slog.i(TAG, "Starting input manager"); nativeStart(mPtr); // Add ourself to the Watchdog monitors. Watchdog.getInstance().addMonitor(this); registerPointerSpeedSettingObserver(); registerShowTouchesSettingObserver(); registerAccessibilityLargePointerSettingObserver(); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); } }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); updatePointerSpeedFromSettings(); updateShowTouchesFromSettings(); updateAccessibilityLargePointerFromSettings(); }
显而易见这里最值得关注的就是InputManager的start()方法了,可惜这个方法并不值得我们如此关心,因为它做的事情很简单,就是启动InputDispatcherThread和InputReaderThread开始监听。
status_t InputManager::start() { status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputDispatcher thread due to error %d.", result); return result; } result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); if (result) { ALOGE("Could not start InputReader thread due to error %d.", result); mDispatcherThread->requestExit(); return result; } return OK; }
那么InputReaderThread线程是怎么和EventHub关联起来的呢?
对于InputReadThread:
启动后循环执行mReader->loopOnce()
loopOnce()中会调用mEventHub->getEvents读取事件
读到了事件就会调用processEventsLocked处理事件
处理完成后调用getInputDevicesLocked获取输入设备信息
调用mPolicy->notifyInputDevicesChanged函数利用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化
***调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDispatcher
bool InputReaderThread::threadLoop() { mReader->loopOnce(); return true; }void InputReader::loopOnce() { …… size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE); { // acquire lock AutoMutex _l(mLock); mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } …… if (oldGeneration != mGeneration) { inputDevicesChanged = true; getInputDevicesLocked(inputDevices); } } // release lock // Send out a message that the describes the changed input devices. if (inputDevicesChanged) { mPolicy->notifyInputDevicesChanged(inputDevices); } …… mQueuedListener->flush(); }
感谢各位的阅读,以上就是“Android中如何利用Input子系统监听线程的启动”的内容了,经过本文的学习后,相信大家对Android中如何利用Input子系统监听线程的启动这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流