Android Activity 接收按键原理分析(2)

按键传递过程
接收按键的传递流程

EventHub用来读取驱动中的event。InputReader负责将EventHub中的消息读取出来,之后InputDispatcher将event发送出来。
具体代码如下

InputReader.cpp
void InputReader::loopOnce() {
	···
	size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);//读取event
	···
	processEventsLocked(mEventBuffer, count);//处理event事件
	···
}

这里处理的event事件有以下四种情况:
1.DEVICE_ADDED添加输入设备
2.DEVICE_REMOVED移除输入设备
3.FINISHED_DEVICE_SCAN输入设备扫描完成
4.普通按键
这里我们只对普通按键过程进行分析

InputReader.cpp
void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) {
	···
	processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
	···
}

void InputReader::processEventsForDeviceLocked(int32_t deviceId,
        const RawEvent* rawEvents, size_t count) {
    ssize_t deviceIndex = mDevices.indexOfKey(deviceId);//查找event对应的设备

    InputDevice* device = mDevices.valueAt(deviceIndex);//获取deviceid对应的设备

    device->process(rawEvents, count);//调用设备的处理函数
}

这里我们以KeyboardInputMapper为例

void KeyboardInputMapper::process(const RawEvent* rawEvent) {
	switch (rawEvent->type) {
		case EV_KEY: {
			if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)){}//获取按键的keycode
			processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags);
			break;
		···
		}
	}
}

void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode,
        int32_t scanCode, uint32_t policyFlags) {
	···
		NotifyKeyArgs args(when, getDeviceId(), mSource, policyFlags,
		down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP,
		AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime);
		getListener()->notifyKey(&args);//这个call InputDispatcher.notifyKey
	···
}

这个里面会调用InputDispatcher里面的notifyKey函数。

InputDispatcher.cpp
void InputDispatcher::notifyKey(const NotifyKeyArgs* args) {
	···
    event.initialize(args->deviceId, args->source, args->action,
            flags, args->keyCode, args->scanCode, metaState, 0,
            args->downTime, args->eventTime);
	//这里面要先call PhoneWindowManager中的interceptKeyBeforeQueueing
    mPolicy->interceptKeyBeforeQueueing(&event, /*byref*/ policyFlags);
	···
	KeyEntry* newEntry = new KeyEntry(args->eventTime,
		args->deviceId, args->source, policyFlags,
		args->action, flags, args->keyCode, args->scanCode,
		metaState, repeatCount, args->downTime);
	//然后将这个keyevent放到list中
	needWake = enqueueInboundEventLocked(newEntry);//将这个event放入到队列中,供后面进行处理(1)
	if (needWake) {//这个会唤醒dispatcher loop.也就是会调dispatchOnce
		mLooper->wake();
    }
}

这里需要注意,PhoneWindowManager里面的interceptKeyBeforeQueueing实在这个阶段调用的。这时候还没加入到queue中。
将Event加入到Queue中之后还要对queue进行处理。后面调用mLooper->wake()的目的就是开始后续的处理。

void InputDispatcher::dispatchOnce() {
	if (!haveCommandsLocked()) {
		ALOGE("no commands");
		dispatchOnceInnerLocked(&nextWakeupTime);
	}
	//此时应该已经将要执行的command放入到了queue中。所以要运行queue中的command
	// Run all pending commands if there are any.
	// If any commands were run then force the next poll to wake up immediately.
	if (runCommandsLockedInterruptible()) {
		nextWakeupTime = LONG_LONG_MIN;
	}    
	nsecs_t currentTime = now();
    int timeoutMillis = toMillisecondTimeoutDelay(currentTime, nextWakeupTime);
    mLooper->pollOnce(timeoutMillis);
}

这个函数比较有意思,下面详细分析一下
先看haveCommandsLocked函数

bool InputDispatcher::haveCommandsLocked() const {
    return !mCommandQueue.isEmpty();
}

这个函数看是否已经存才command,第一次执行会返回false。
下面看dispatchOnceInnerLocked

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
	···
	    case EventEntry::TYPE_KEY: {
			···
			done = dispatchKeyLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
			···
		}
	···
}

bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry,
        DropReason* dropReason, nsecs_t* nextWakeupTime) {
	if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) {
        if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) {
            CommandEntry* commandEntry = postCommandLocked(
                    & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible);
            if (mFocusedWindowHandle != NULL) {
                commandEntry->inputWindowHandle = mFocusedWindowHandle;
            }
            commandEntry->keyEntry = entry;
            entry->refCount += 1;
            return false; // wait for the command to run
        } else {
           ···
        }
    } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) {
		···
    }
	···
	int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime,entry, inputTargets, nextWakeupTime);//这个函数用来查找有哪些对象已经建立了connection。event将会发送给所有的注册进来的对象。也就是activity。
	···
	dispatchEventLocked(currentTime, entry, inputTargets);
	···
}

postCommandLocked函数就是向mCommandQueue中添加command的实现如下:

InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) {
    CommandEntry* commandEntry = new CommandEntry(command);
    mCommandQueue.enqueueAtTail(commandEntry);
    return commandEntry;
}

执行完这个函数只有command queue中已经存在command,但是还没执行。

下面继续分析

void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
        EventEntry* eventEntry, const Vector<InputTarget>& inputTargets) {


    pokeUserActivityLocked(eventEntry);//用来决定是否要唤醒设备或者点亮屏幕,最终调用的是PowerManagerService。
	//下面将会通知所有注册进来的channen,有event发生了。
    for (size_t i = 0; i < inputTargets.size(); i++) {
        const InputTarget& inputTarget = inputTargets.itemAt(i);

        ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel);
        if (connectionIndex >= 0) {
            sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
        } else {
			···
        }
    }
}

下面的调用顺序为:prepareDispatchCycleLocked-> enqueueDispatchEntriesLocked &startDispatchCycleLocked
这里面有个小的细节说明一下:enqueueDispatchEntriesLocked函数会调用enqueueDispatchEntryLocked函数。

void InputDispatcher::enqueueDispatchEntryLocked(
        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
        int32_t dispatchMode) {
	···
	DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref
	inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
	inputTarget->scaleFactor);
	···
	connection->outboundQueue.enqueueAtTail(dispatchEntry);
	···
}

这里会封装DispatchEntry,之后放到connection,也就是server socket的封装对象的outboundQueue中。那么在后面发送的时候就会从这里面取event。
继续后面的分析

void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection) {
	···
	 case EventEntry::TYPE_KEY: {
		KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

		// Publish the key event.
		// call InputTransport.cpp
		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;
	}
	···
}

这里会调用InputTransport中的publishKeyEvent

status_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}

status_t InputChannel::sendMessage(const InputMessage* msg) {
	···
	do {
        nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
    } while (nWrite == -1 && errno == EINTR);
	···
}

也就是调用socket的send函数。这里的mFd对应的是Server端的socket。
由于Server/client的socket是通过socketpair的方式打开的,所以向Server端socket写入数据时,client端的socket就能读取数据。
根据之前的分析可知client端的socket加入到了epoll中ALOOPER_EVENT_INPUT。所以它将会从epoll中返回。
处理代码

android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
···
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? 1 : 0;
    }
	···
	env->CallVoidMethod(receiverObj.get(),
		gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);//调用InputManagerService.dispatchInputEvent
	···
···
}

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
		···
		for (;;) {
			status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent);
		}
		···
		}
InputTransport.cpp
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
        bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
		    while (!*outEvent) {
				status_t result = mChannel->receiveMessage(&mMsg);
			}
			···
			switch (mMsg.header.type) {
			case InputMessage::TYPE_KEY: {
				KeyEvent* keyEvent = factory->createKeyEvent();
				if (!keyEvent) return NO_MEMORY;

				initializeKeyEvent(keyEvent, &mMsg);
				*outSeq = mMsg.body.key.seq;
				*outEvent = keyEvent;
				break;
			}
			···
			env->CallVoidMethod(receiverObj.get(),
		gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);//调用InputManagerService.dispatchInputEvent
		···
		}

看到在获取完event之后就调用了dispatchInputEvent。由前面的分析可以知道,在新的view生成之后,注册client socket时创建了WindowInputEventReceiver,这个类是继承自InputEventReceiver。所以这里面调用的dispatchInputEvent就是WindowInputEventReceiver。根据类的继承关系,在dispatchInputEvent中会调用onInputEvent。
接下来的就是在ViewRootImpl中对event进行处理。

ViewRootImpl.java
final class WindowInputEventReceiver extends InputEventReceiver {
	···
	@Override
	public void onInputEvent(InputEvent event) {
		enqueueInputEvent(event, this, 0, true);
	}
	···
}

void enqueueInputEvent(InputEvent event,
		InputEventReceiver receiver, int flags, boolean processImmediately) {
	//将event加入到队列中
	QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
	QueuedInputEvent last = mPendingInputEventTail;
	if (last == null) {
		mPendingInputEventHead = q;
		mPendingInputEventTail = q;
	} else {
		last.mNext = q;
		mPendingInputEventTail = q;
	}
	mPendingInputEventCount += 1;
	//处理event
	if (processImmediately) {
		doProcessInputEvents();//处理event
	} else {
		scheduleProcessInputEvents();
	}
}

void doProcessInputEvents() {
	// Deliver all pending input events in the queue.
	while (mPendingInputEventHead != null) {
		QueuedInputEvent q = mPendingInputEventHead;
		mPendingInputEventHead = q.mNext;
		if (mPendingInputEventHead == null) {
			mPendingInputEventTail = null;
		}
		q.mNext = null;

		mPendingInputEventCount -= 1;
		Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
				mPendingInputEventCount);

		deliverInputEvent(q);
	}
	···
}
//决定应该是传递给那个stage,之后调用stage.deliver()开始处理input event。
private void deliverInputEvent(QueuedInputEvent q) {

		InputStage stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
		if (stage != null) {
			stage.deliver(q);
		} else {
			finishInputEvent(q);
		}
	} finally {
		Trace.traceEnd(Trace.TRACE_TAG_VIEW);
	}
}

通过上面的分析可以发现,jni层获取到消息之后,会传递给framework层的InputEventReceiver。之后交给ViewRootImpl中的stage进行按键的处理。下面对stage进行一下分析

ViewRootImpl中stage顺序

这里面的Stage是一个逻辑结构,类似于StateMachine。当有InputEvent之后将会传递到这个结构中进行处理。处理完成之后,会调用finishInputEvent函数来通知jni层,这个event已经处理完成。
各个Stage之间的关系如下图所示:
ViewRootImpl中各个stage的关系
指定的初始化stage

mFirstInputStage = nativePreImeStage;//按键处理的第一个stage
mFirstPostImeInputStage = earlyPostImeStage;

根据打印的log看,当接收到遥控器按键时将会调用mView.dispatchKeyEvent(),而这里的mView就是在创建Activity是通过setView设置进来的。当这个InputEvent事件处理完成之后,会调用finishInputEvent函数。

ViewRootImpl.java
private void finishInputEvent(QueuedInputEvent q) {
	if (q.mReceiver != null) {
		boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
		q.mReceiver.finishInputEvent(q.mEvent, handled);
	} else {
		q.mEvent.recycleIfNeededAfterDispatch();
	}

	recycleQueuedInputEvent(q);
}
public final void finishInputEvent(InputEvent event, boolean handled) {

	int index = mSeqMap.indexOfKey(event.getSequenceNumber());
	if (index < 0) {
		Log.w(TAG, "Attempted to finish an input event that is not in progress.");
	} else {
		int seq = mSeqMap.valueAt(index);
		mSeqMap.removeAt(index);
		nativeFinishInputEvent(mReceiverPtr, seq, handled);
	}
	
	event.recycleIfNeededAfterDispatch();
}

看到,会调用nativeFinishInputEvent进行处理,也就是会调用NativeInputEventReceiver.finishInputEvent()处理。

android_view_InputEventReceiver.cpp
status_t NativeInputEventReceiver::finishInputEvent(uint32_t seq, bool handled) {
	···
	status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
	···
}

这里面的mInputConsumer与之前发送InputEvent是一样的,

status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
	···
	return sendUnchainedFinishedSignal(seq, handled);
}

status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_FINISHED;
    msg.body.finished.seq = seq;
    msg.body.finished.handled = handled;
    return mChannel->sendMessage(&msg);
}

通过这部分代码,可以看出最后是通过client端的socket,将finish事件发送给jni层中的server端的。前面的分析可以知道,在注册server端的socket时,将socket注册到epoll中,并且指定有event时的处理函数为handleReceiveCallback。

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
	//根据fd查找到对应的connection
	ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
	sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
	···
	//开始下一轮的dispatch
	status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
	d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
	
	if (gotOne) {
		d->runCommandsLockedInterruptible();
		if (status == WOULD_BLOCK) {
			return 1;
		}
	}
	
	···
	d->unregisterInputChannelLocked(connection->inputChannel, notify);
	
}

至此,InputEvent的整个处理过程已经完全结束。

总结

Input event事件的处理,大概流程如下:
1.EventHub从驱动中获取InputEvent
2.InputReader从EventHub中读取数据发送给InputDispatcher
3.InputDispatcher发送给InputTransport,
4.InputTransport将消息发送给framework层的ViewRootImpl
5.ViewRootImpl对事件进行处理,完成之后发送finish给native层,用来启动下一轮的dispatch
事件是通过注册到epoll中的Server/Client sockets进行传递的。
发送event时会发送到所有的channel中的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值