Android Input 子系统: Input 进程的创建,监听线程的启动 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Cheelok
V2EX    Android

Android Input 子系统: Input 进程的创建,监听线程的启动

  •  
  •   Cheelok 2017-09-19 22:21:31 +08:00 11959 次点击
    这是一个创建于 3002 天前的主题,其中的信息可能已经有所发展或是发生改变。

    专栏地址: https://zhuanlan.zhihu.com/p/29152319

    从我个人的理解来看,Android 的 Input 系统其实是系统级的事件处理、分发框架,它需要的功能模块大致有:事件读取、事件分类、事件分发。那么我们就从整个 Input 系统的输入源入手,了解事件是如何被输入到 Input 系统中的。

    在看代码前我们先想一想,如果要我们设计一个事件分发框架的输入读取模块,要考虑到哪些子模块:

    1. 事件生成模块(当用户对设备进行操作产生 InputEvent,硬件产生中断将事件交给驱动,驱动交给内核,内核交给 framework )
    2. 事件监听模块(这里就很像设计一个服务器,为了及时响应来自客户端的请求,则需要启动一个线程监听)
    3. 事件读取模块
    4. 事件分发模块

    那么现在我们最起码可以知道整个学习的起点了,就是 Input 系统中,负责监听的线程是谁,监听的过程中它们做了什么。在开始之前,给大家分享一张我根据本文内容画的图:

    InputManagerService 初始化概览

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

    1. Android Framework 层的 Service ( Java )都是由 system_server 进程创建的(由于没有 fork,因此都运行在 system_server 进程中)
    2. Service 创建后就会交给运行在 system_server 进程中的 ServiceManager 管理。

    因此对于 InputManagerService 的创建,我们可以在 SystemServer 的 startOtherServices()方法中找到,该方法做了以下事情:

    1. 创建 InputManagerService 对象
    2. 将它交给 ServiceManager 管理
    3. 将 WindowManagerService 的 InputMonitor 注册到 InputManagerService 中作为窗口响应事件后的回调
    4. 完成以上工作后启动 InputManagerService。
    SystemServer.java startOtherServices(){ …… inputManager = new InputManagerService(context); …… inputManager.setWindowManagerCallbacks(wm.getInputMonitor()); inputManager.start(); …… } 

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

    InputManagerService 对象的创建

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

    1. 创建一个负责处理 DisplayThread 线程中的 Message 的 Handler
    2. 调用 nativeInit 初始化 native 层的 InputManagerService,初始化的时候传入了 DisplayThread 的消息队列
    3. 用 mPtr 保存 native 层的 InputManagerService
    4. 初始化完成后将 Service 添加到 LocalServices,通过 Map 以键值对的形式存储
    InputManagerService.java public 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 的创建,这个过程做了以下事情:

    1. 将 Java 层的 Context 和 InputManagerService 转换为 native 层的 Context 和 InputManagerService 存储在 mContextObj 和 mServiceObj 中
    2. 初始化变量
    3. 创建 EventHub
    4. 创建 InputManager
    com_android_server_input_InputManagerService.cpp NativeInputManager::NativeInputManager(jobject contextObj, jobject serviceObj, const sp<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 = new EventHub(); mInputManager = new InputManager(eventHub, this, this); } 

    EventHub

    看到这里很多人就会想,EventHub 是什么?取英语释义来看,它的意思是事件枢纽。我们在文章开头的时候也提到过,Input 系统的事件来源于驱动 /内核,那么我们可以猜测 EventHub 是处理来自驱动 /内核的元事件的枢纽。接下来就在源码中验证我们的想法吧。

    EventHub 的创建过程中做了以下事情:

    1. 创建 mEpollFd 用于监听是否有数据(有无事件)可读
    2. 创建 mINotifyFd 将它注册到 DEVICE_PATH (这里路径就是 /dev/input )节点,并将它交给内核用于监听该设备节点的增删数据事件。那么只要有数据增删的事件到来,epoll_wait()就会返回,使得 EventHub 能收到来自系统的通知,并获取事件的详细信息
    3. 调用 epoll_ctl 函数将 mEpollFd 和 mINotifyFd 注册到 epoll 中
    4. 定义 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<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& readerPolicy, const sp<InputDispatcherPolicyInterface>& 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()方法中,做了以下事情:

    1. 调用 nativeStart 方法,其实就是调用 InputManager 的 start()方法
    2. 将 InputManagerService 交给 WatchDog 监控
    3. 注册触控点速度、显示触控的观察者,并注册广播监控它们
    4. 主动调用 updateXXX 方法更新(初始化)
    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(); 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:

    1. 启动后循环执行 mReader->loopOnce()
    2. loopOnce()中会调用 mEventHub->getEvents 读取事件
    3. 读到了事件就会调用 processEventsLocked 处理事件
    4. 处理完成后调用 getInputDevicesLocked 获取输入设备信息
    5. 调用 mPolicy->notifyInputDevicesChanged 函数利用 InputManagerService 的代理通过 Handler 发送 MSG_DELIVER_INPUT_DEVICES_CHANGED 消息,通知输入设备发生了变化
    6. 最后调用 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(); } 

    至此,Input 进程的创建,监听线程的启动相关学习就结束了。

    目前尚无回复
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3003 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 13:11 PVG 21:11 LAX 05:11 JFK 08:11
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86