android 4.2 input 流程分析,Android Input流程

38f2da61973d

38f2da61973d

一.input 系统初始化

安卓系统启动时,会开启SystemServer进程,SystemServer执行main函数,调用startOtherService()初始化windowManagerService和InputManagerService等

InputManagerService的构造函数如下,mPtr是一个指向nativeInputManager对象的指针.

mPtr = nativeInit(this, mContext, mHandler.getLooper().getQueue());

LocalServices.addService(InputManagerInternal.class, new LocalService());

这里的nativeInit方法调用到com_android_server_input_InputManagerService.cpp中NativeInputManager的构造函数

这里会构造一个EventHub对象并将它传入InputManager的构造函数中,

321 sp eventHub = new EventHub();

322 mInputManager = new InputManager(eventHub, this, this);

27InputManager::InputManager(

28 const sp& eventHub,

29 const sp& readerPolicy,

30 const sp& dispatcherPolicy) {

31 mDispatcher = new InputDispatcher(dispatcherPolicy);

32 mReader = new InputReader(eventHub, readerPolicy, mDispatcher);

33 initialize();

34}

initialize函数中会设置两个线程来运行InputReader和InputDispatcher

49 mReaderThread = new InputReaderThread(mReader);

50 mDispatcherThread = new InputDispatcherThread(mDispatcher);

inputManagerService会设置一个回调,供windowManager调用,之后会调用inputManager的start()方法

wm = WindowManagerService.main(context, inputManager,

mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,

!mFirstBoot, mOnlyCore);

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

inputManager.start();

start方法会最终调用到InputManager.cpp 文件中的 status_t InputManager::start()函数

主要步骤如下,运行dispatcherThread的run()方法和mReaderThread的run()方法.

54 status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);

55 if (result) {

56 ALOGE("Could not start InputDispatcher thread due to error %d.", result);

57 return result;

58 }

59

60 result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);

61 if (result) {

62 ALOGE("Could not start InputReader thread due to error %d.", result);

63

64 mDispatcherThread->requestExit();

65 return result;

66 }

67

68 return OK;

38f2da61973d

新建位图图像.jpg

二.读取input events

mReaderThread->run()方法会调用自身的 threadLoop()方法,进而调用mReader的loopOnce()方法,

loopOnce()方法主要分为三个主要的步骤.

步骤一:

在该方法中会通过EventHub来获取input事件,计算缓冲区大小,判断是否有新的事件产生,然后用processEventsLocked()方法来处理events

size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);

314 if (count) {

315 processEventsLocked(mEventBuffer, count);

316 }

步骤二:

在processEventsLocked()方法中会调用processEventsForDeviceLocked(deviceId, rawEvent, batchSize)函数,该函数会确定设备编号并调用

531 device->process(rawEvents, count);

安卓系统中每种输入设备都对应了一种Mapper,比如SwitchInputMapper, VibratorInputMapper,KeyBoardInputMapper

在process()函数中会调用对应mapper的processKey()函数,在该函数中具体处理不同的input事件

在processKey()函数中会调用 getListener()->notifyKey(), 也就是调用mQueuedListener.notifyKey();

而这个listener的notifyKey()方法只有一行代码,将notifyKeyArgs对象放入到mArgsQueue队列中.

mArgsQueue.push(**new** NotifyKeyArgs(*args));

步骤三:

最后一步会将mQueuedListener队列中的所有内容全部清空.

mQueuedListener->flush();

flush()函数中将所有的NotifyArgs对象取出,依次执行args->notify()

171void QueuedInputListener::flush() {

172 size_t count = mArgsQueue.size();

173 for (size_t i = 0; i < count; i++) {

174 NotifyArgs* args = mArgsQueue[i];

175 args->notify(mInnerListener);

176 delete args;

177 }

178 mArgsQueue.clear();

179}

而notify()函数会调用innnerListener的notifyKey()函数,innerListener实际上就是构造InputReader时传入的InputDispatcher,所以调用的就是InputDispatcher的notifyKey()函数

62void NotifyKeyArgs::notify(const sp& listener) const {

63 listener->notifyKey(this);

64}

在notifyKey()函数中会新建一个KeyEvent对象并进行初始化,然后调用mPolicy的interceptKeyBeforeQueueing__(mPolicy就是NativeInputManagers)

2548 KeyEvent event;

2549 event.initialize(args->deviceId, args->source, args->action,

2550 flags, keyCode, args->scanCode, metaState, 0,

2551 args->downTime, args->eventTime);

2552

2553 mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);

这里会在c++代码中调用java层的代码,会先把keyEvent转换为jobject对象,然后调用java层对应的interceptBeforeQueueing函数

894 jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent);

896 if (keyEventObj) {

897 wmActions = env->CallIntMethod(mServiceObj,

898 gServiceClassInfo.interceptKeyBeforeQueueing,

899 keyEventObj, policyFlags);

这里调用了native的interceptKeyBeforeQueueing函数,用到了在初始化时设定的一个windowManager持有的回调.

// Native callback.

private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {

return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);

}

通过这个windowManager持有的回调,最终会调用到WindowPhoneManager的interceptKeyBeforeQueueing函数(mpolicy就是windowPhoneManager),这样以来对input的管理最终集中到了windowManager中.

/* Provides an opportunity for the window manager policy to intercept early key

* processing as soon as the key has been read from the device. */

@Override

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {

return mService.mPolicy.interceptKeyBeforeQueueing(event, policyFlags);

}

input events从java层的windowManagerService的拦截函数返回后,会继续在notify()函数中运行,这里会继续判断是否打开了过滤器开关,如果打开了就对event进行过滤,并返回

2559 if (shouldSendKeyToInputFilterLocked(args)) {

2560 mLock.unlock();

2561

2562 policyFlags |= POLICY_FLAG_FILTERED;

2563 if (!mPolicy->filterInputEvent(&event, policyFlags)) {

2564 return; // event was consumed by the filter

2565 }

2566

2567 mLock.lock();

2568 }

将event信息存储在一个KeyEntry对象中,调用enqueueInboundEventLocked函数并判断是否需要唤醒

2571 KeyEntry* newEntry = new KeyEntry(args->eventTime,

2572 args->deviceId, args->source, policyFlags,

2573 args->action, flags, keyCode, args->scanCode,

2574 metaState, repeatCount, args->downTime);

2575

2576 needWake = enqueueInboundEventLocked(newEntry);

之后如果需要唤醒,就会调用mLooper.wake()唤醒分发线程进行事件分发.

三.分发events到UI窗口

回到InputDispatcher中分析:

mDispatcherThread::threadLoop()方法中会调用dispatchOnce()函数,该函数中主要调用两个方法一个是

DispatchOnceInnerLocked()函数.

319 if (!haveCommandsLocked()) {

320 dispatchOnceInnerLocked(&nextWakeupTime);

321 }

主要会调用到pokeUserActivityLocked()函数

394 // Poke user activity for this event.

395 if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {

396 pokeUserActivityLocked(mPendingEvent);

397 }

在 pokeUserActivityLocked()函数中会将key事件封装成系统需要处理的key事件.

1902 CommandEntry* commandEntry = postCommandLocked(

1903 & InputDispatcher::doPokeUserActivityLockedInterruptible);

1904 commandEntry->eventTime = eventEntry->eventTime;

1905 commandEntry->userActivityEventType = eventType;

在这里会调用 doPokeUserActivityLockedInterruptible函数,函数中会调用NativeInputManager的pokeUserActivity函数

mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType);

通过JNI调用到java层PowerManagerService中的 userActivityFromNative函数,

106 env->CallVoidMethod(gPowerManagerServiceObj,

107 gPowerManagerServiceClassInfo.userActivityFromNative,

108 nanoseconds_to_milliseconds(eventTime), eventType, 0);

private void userActivityInternal(long eventTime, int event, int flags, int uid) {

synchronized (mLock) {

if (userActivityNoUpdateLocked(eventTime, event, flags, uid)) {

updatePowerStateLocked();

}

}

}

回到dispatchOnceInnerLocked函数之后会对event的类型进行判断,按照event的类型进行不同的操作.

首先会调用 findFocusedWindowTargetLocked函数得到要对应的UI窗口

875 int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,

876 entry, inputTargets, nextWakeupTime);

如果是按键事件,会调用dispatchKeyLocked()函数,又会调用dispatchEventLocked()函数:

确定对应输出的UI窗口,并得到它们之间的connection.

1029 const InputTarget& inputTarget = inputTargets.itemAt(i);

1030

1031 ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);

之后会进入到enqueueDispatchEntriesLocked函数中.

1945 enqueueDispatchEntriesLocked(currentTime, connection,

1946 splitMotionEntry, inputTarget);

1947 splitMotionEntry->release();

connection中维护一个outboundQueue队列,这个队列中存储input与该window之间的事件.

如果队列为空,就开始循环分发.

1974 // If the outbound queue was previously empty, start the dispatch cycle going.

1975 if (wasEmpty && !connection->outboundQueue.isEmpty()) {

1976 startDispatchCycleLocked(currentTime, connection);

1977 }

在startDisplayCycleLocked函数中:

会不断地取出事件进行分发,具体的分发过程利用到了linux的epoll机制,后面有具体分析.

status = connection->inputPublisher.publishMotionEvent

操作完成后会释放掉pendingEvent

478 if (done) {

479 if (dropReason != DROP_REASON_NOT_DROPPED) {

480 dropInboundEventLocked(mPendingEvent, dropReason);

481 }

482 mLastDropReason = dropReason;

483

484 releasePendingEventLocked();

485 *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately

486 }

Looper利用epoll机制接收events,并调用callback回调的handleEvent方法.也就是NativeInputEventReceiver的handleEvent() 方法

361 // Invoke the callback. Note that the file descriptor may be closed by

362 // the callback (and potentially even reused) before the function returns so

363 // we need to be a little careful when removing the file descriptor afterwards.

364 int callbackResult = response.request.callback->handleEvent(fd, events, data);

而为什么会调用到这个InputEventReciever呢,InputEventReceiver是在ViewRootImpl中的setView()方法中被声明的,它的构造函数中有两个变量: mInputChannel和 Looper.myLooper()

mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());

而在InputEventReceiver的构造函数中,会先取得主线程的looper对象和client端的java层inputChannel对象,然后调用NativeInit()方法进行初始化.

mInputChannel = inputChannel;

mMessageQueue = looper.getQueue();

mReceiverPtr = nativeInit(new WeakReference(this),

inputChannel, mMessageQueue);

在JNI层,首先获得了inputChannel和messageQueue的指针,并构造一个NativeInputEventReceiver对象.

352 sp inputChannel = android_view_InputChannel_getInputChannel(env,

353 inputChannelObj);

359 sp messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);

365 sp receiver = new NativeInputEventReceiver(env,

366 receiverWeak, inputChannel, messageQueue);

之后会调用receiver的initialize()方法.

122status_t NativeInputEventReceiver::initialize() {

123 setFdEvents(ALOOPER_EVENT_INPUT);

124 return OK;

125}

setFdEvents函数中,会将client端的inputChannel的socket文件描述符加入到Looper的监控中.

162void NativeInputEventReceiver::setFdEvents(int events) {

163 if (mFdEvents != events) {

164 mFdEvents = events;

165 int fd = mInputConsumer.getChannel()->getFd();

166 if (events) {

167 mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);

168 } else {

169 mMessageQueue->getLooper()->removeFd(fd);

170 }

171 }

172}

173

38f2da61973d

InitInputChannel.jpg

当有消息到来的时候,会调用inputChannel对应的NativeInputEventReceiver回调函数.

它的handleEvent()函数中会调用consumeEvents函数,

status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);

在 consumeEvents()函数中会调用到InputChannel的receiveMessage方法.

493 // Receive a fresh message.

494 status_t result = mChannel->receiveMessage(&mMsg);

然后通过JNI调用到java层InputEventReceiver的dispatchInputEvent()回调函数.

330 env->CallVoidMethod(receiverObj.get(),331 gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);

private void dispatchInputEvent(int seq, InputEvent event) {

mSeqMap.put(event.getSequenceNumber(), seq);

onInputEvent(event);

}

然后调用到ViewRoot的OnInputEvent()方法,继而调用了enqueuInputEvent()方法,

if (processImmediately) {

doProcessInputEvents();

} else {

scheduleProcessInputEvents();

}

之后进入到delieverInputEvent()方法中,对stage进行判断,如果为空则结束分发,否则

if (stage != null) {

stage.deliver(q);

} else {

finishInputEvent(q);

}

deliver()函数中会执行stage的onPorcess()方法.

apply(q, onProcess(q));

这里的stage时这样取值的

if (q.shouldSendToSynthesizer()) {

stage = mSyntheticInputStage;

} else {

stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;

}

这里InputStage的设计用到了流水线或者叫

责任链的模式

38f2da61973d

InputStage结构.jpg

InputStage有很多子类,所有InputStage类的构造方法都会传入一个InputStage类的变量,这样最终会形成流水线线式的处理结构,每经过一个InputStage对象的处理都会进行判断,看是否还需要将 events继续向前传输,如果需要就调用forward()函数让该变量中存储的下一个InputStage对象处理该events,如果不需要就调用finish()函数结束events的传输.

只需知道最终会调用到ViewRootImpl的enqueueInputEvent()方法.

该方法中会立即或者延迟处理events.

if (processImmediately) {

doProcessInputEvents();

} else {

scheduleProcessInputEvents();

}

在 doProcessInputEvent()方法中会判断是否要经过IME输入法框架处理.

stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;

最后经过判断后会层层处理,最终进入到view.dispatchKeyEvent()方法中.

四.

最后返回到dispatchOnceLocked()函数中,执行runCommandsLockedInterruptible()函数.

323 // Run all pending commands if there are any.

324 // If any commands were run then force the next poll to wake up immediately.

325 if (runCommandsLockedInterruptible()) {

326 nextWakeupTime = LONG_LONG_MIN;

327 }

五.Epoll机制

38f2da61973d

epoll.jpg

Looper->wake()函数利用了linux中的epoll机制。

首先,注册一个epoll的实例描述符,将所有的管道对象的fd都注册到该epoll实例上,利用epoll_wait函数来睡眠等待管道上IO事件的发生;

调用PollOnce()函数来启动epoll_wait的睡眠等待,而wake()函数则是向epoll中的管道写入一个字符来唤醒epoll_wait.

六.InputChannel分析

38f2da61973d

38f2da61973d

InputChannel结构.jpeg

首先是server端的inputChannel注册

首先打开一对InputChannelPair,调用到JNI中的

android_view_InputChannel_nativeOpenInputChannelPair方法,最终会调用到linux中的socketPair()函数打开一对socket管道,而inputChannel就是由socket管道来构建的.

184 outServerChannel = **new** InputChannel(serverChannelName, sockets[0]);

inputChannel在WMS中注册,保存在一个WindowState的类中,WMS使用这对inputChannel的第一个

然后会调用registerInputChannel方法进行注册.

mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);

最终会调用Native层的方法

nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);

将inputChannel和connection保存在映射表中,mLooper加入对该fd的监听,当有数据到来的时候会唤醒等待的线程.

3511 sp connection = new Connection(inputChannel, inputWindowHandle, monitor);

3512

3513 int fd = inputChannel->getFd();

3514 mConnectionsByFd.add(fd, connection);

3516 if (monitor) {

3517 mMonitoringChannels.push(inputChannel);

3518 }

3519

3520 mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);

而当InputDispatcher分发消息时,会调用InputChannel::sendMessage()方法,该方法调用了linux socket中的send函数,向socket管道中写入数据.

client端inputChannel的注册

WMS中创建的inputChannel跨进程传输到cilent端,转换为client端的inputChannel使用.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值