Android中如何利用Input子系统监听线程的启动

Android中如何利用Input子系统监听线程的启动

这篇文章主要讲解了“Android中如何利用Input子系统监听线程的启动”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android中如何利用Input子系统监听线程的启动”吧!

InputManagerService初始化概览

Android中如何利用Input子系统监听线程的启动

首先,有几点共识我们都可以达成:

  • 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=newInputManagerService(context);……inputManager.setWindowManagerCallbacks(wm.getInputMonitor());inputManager.start();……}

接下来我们就逐部分学习相应的处理。

InputManagerService对象的创建

创建InputManagerService对象时会完成以下工作:

  • 创建一个负责处理DisplayThread线程中的Message的Handler

  • 调用nativeInit初始化native层的InputManagerService,初始化的时候传入了DisplayThread的消息队列

  • 用mPtr保存native层的InputManagerService

  • 初始化完成后将Service添加到LocalServices,通过Map以键值对的形式存储

InputManagerService.javapublicInputManagerService(Contextcontext){this.mContext=context;this.mHandler=newInputManagerHandler(DisplayThread.get().getLooper());mUseDevInputEventForAudioJack=context.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);Slog.i(TAG,"Initializinginputmanager,mUseDevInputEventForAudioJack="+mUseDevInputEventForAudioJack);mPtr=nativeInit(this,mContext,mHandler.getLooper().getQueue());LocalServices.addService(InputManagerInternal.class,newLocalService());}

这里可能有人就会问了,为什么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.cppNativeInputManager::NativeInputManager(jobjectcontextObj,jobjectserviceObj,constsp<Looper>&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>eventHub=newEventHub();mInputManager=newInputManager(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.cppEventHub::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,"Couldnotcreateepollinstance.errno=%d",errno);mINotifyFd=inotify_init();intresult=inotify_add_watch(mINotifyFd,DEVICE_PATH,IN_DELETE|IN_CREATE);&hellip;&hellip;result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mINotifyFd,&eventItem);&hellip;&hellip;intwakeFds[2];result=pipe(wakeFds);&hellip;&hellip;mWakeReadPipeFd=wakeFds[0];mWakeWritePipeFd=wakeFds[1];result=fcntl(mWakeReadPipeFd,F_SETFL,O_NONBLOCK);&hellip;&hellip;result=fcntl(mWakeWritePipeFd,F_SETFL,O_NONBLOCK);&hellip;&hellip;result=epoll_ctl(mEpollFd,EPOLL_CTL_ADD,mWakeReadPipeFd,&eventItem);&hellip;&hellip;}

那么这里抛出一个问题:为什么要把管道的读端注册到epoll中?假如EventHub因为getEvents读不到事件而阻塞在epoll_wait()里,而我们没有绑定读端的话,我们要怎么唤醒EventHub?如果绑定了管道的读端,我们就可以通过向管道的写端写数据从而让EventHub因为得到管道写端的数据而被唤醒。

InputManager的创建

接下来继续说InputManager的创建,它的创建就简单多了,创建一个InputDispatcher对象用于分发事件,一个InputReader对象用于读事件并把事件交给InputDispatcher分发,,然后调用initialize()初始化,其实也就是创建了InputReaderThread和InputDispatcherThread。

InputManager.cppInputManager::InputManager(constsp<EventHubInterface>&eventHub,constsp<InputReaderPolicyInterface>&readerPolicy,constsp<InputDispatcherPolicyInterface>&dispatcherPolicy){mDispatcher=newInputDispatcher(dispatcherPolicy);mReader=newInputReader(eventHub,readerPolicy,mDispatcher);initialize();}voidInputManager::initialize(){mReaderThread=newInputReaderThread(mReader);mDispatcherThread=newInputDispatcherThread(mDispatcher);}

InputDispatcher和InputReader的创建都相对简单。InputDispatcher会创建自己线程的Looper,以及设置根据传入的dispatchPolicy设置分发规则。InputReader则会将传入的InputDispatcher封装为监听对象存起来,做一些数据初始化就结束了。

至此,InputManagerService对象的初始化就完成了,根据开头说的,接下来就会调用InputManagerService的start()方法。

监听线程InputReader和InputDispatcher的启动

在start()方法中,做了以下事情:

  • 调用nativeStart方法,其实就是调用InputManager的start()方法

  • 将InputManagerService交给WatchDog监控

  • 注册触控点速度、显示触控的观察者,并注册广播监控它们

  • 主动调用updateXXX方法更新(初始化)

InputManagerService.javapublicvoidstart(){Slog.i(TAG,"Startinginputmanager");nativeStart(mPtr);//AddourselftotheWatchdogmonitors.Watchdog.getInstance().addMonitor(this);registerPointerSpeedSettingObserver();registerShowTouchesSettingObserver();registerAccessibilityLargePointerSettingObserver();mContext.registerReceiver(newBroadcastReceiver(){@OverridepublicvoidonReceive(Contextcontext,Intentintent){updatePointerSpeedFromSettings();updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();}},newIntentFilter(Intent.ACTION_USER_SWITCHED),null,mHandler);updatePointerSpeedFromSettings();updateShowTouchesFromSettings();updateAccessibilityLargePointerFromSettings();}

显而易见这里最值得关注的就是InputManager的start()方法了,可惜这个方法并不值得我们如此关心,因为它做的事情很简单,就是启动InputDispatcherThread和InputReaderThread开始监听。

status_tInputManager::start(){status_tresult=mDispatcherThread->run("InputDispatcher",PRIORITY_URGENT_DISPLAY);if(result){ALOGE("CouldnotstartInputDispatcherthreadduetoerror%d.",result);returnresult;}result=mReaderThread->run("InputReader",PRIORITY_URGENT_DISPLAY);if(result){ALOGE("CouldnotstartInputReaderthreadduetoerror%d.",result);mDispatcherThread->requestExit();returnresult;}returnOK;}

那么InputReaderThread线程是怎么和EventHub关联起来的呢?

对于InputReadThread:

  • 启动后循环执行mReader->loopOnce()

  • loopOnce()中会调用mEventHub->getEvents读取事件

  • 读到了事件就会调用processEventsLocked处理事件

  • 处理完成后调用getInputDevicesLocked获取输入设备信息

  • 调用mPolicy->notifyInputDevicesChanged函数利用InputManagerService的代理通过Handler发送MSG_DELIVER_INPUT_DEVICES_CHANGED消息,通知输入设备发生了变化

  • ***调用mQueuedListener->flush(),将事件队列中的所有事件交给在InputReader中注册过的InputDispatcher

boolInputReaderThread::threadLoop(){mReader->loopOnce();returntrue;}voidInputReader::loopOnce(){&hellip;&hellip;size_tcount=mEventHub->getEvents(timeoutMillis,mEventBuffer,EVENT_BUFFER_SIZE);{//acquirelockAutoMutex_l(mLock);mReaderIsAliveCondition.broadcast();if(count){processEventsLocked(mEventBuffer,count);}&hellip;&hellip;if(oldGeneration!=mGeneration){inputDevicesChanged=true;getInputDevicesLocked(inputDevices);}}//releaselock//Sendoutamessagethatthedescribesthechangedinputdevices.if(inputDevicesChanged){mPolicy->notifyInputDevicesChanged(inputDevices);}&hellip;&hellip;mQueuedListener->flush();}

感谢各位的阅读,以上就是“Android中如何利用Input子系统监听线程的启动”的内容了,经过本文的学习后,相信大家对Android中如何利用Input子系统监听线程的启动这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是恰卡编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

发布于 2022-04-11 21:16:15
收藏
分享
海报
0 条评论
31
上一篇:Android中如何处理后台崩溃 下一篇:Android中如何实现矩形区域截屏
目录

    0 条评论

    本站已关闭游客评论,请登录或者注册后再评论吧~

    忘记密码?

    图形验证码