了解更多,移步Android触摸事件传递机制系列详解
- 在Android触摸事件的传递(四)--输入系统-InputReaderThread已经讲到通过InputChannel将消息发送出。
-
InputReaderThread
和InputDispatcherThread
是运行在SystemServer进程中的 - 我们的应用进程是和其不在同一个进程中的
- 这之间一定也是有进程间的通信机制在里面
1 InputChannel的创建
-
InputChannel
的创建是在ViewRootImpl
中setView
方法中。 - 首先是创建了一个InputChannel,然后将其调用了WindowSession的addToDisplay方法将其作为参数传递。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
....
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//首先是创建了一个InputChannel
mInputChannel = new InputChannel();
}
....
//将InputChannel添加到WindowManagerService中创建socketpair(一对socket)用来发送和接受事件
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
....
//开启了对于InputChannel中输入事件的监听
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
...
}
2 将InputChannel添加到WindowManagerService中创建socketpair(一对socket)用来发送和接受事件
-
addToDisplay
将会把InputChannel
添加到WindowManagerService
中。会调用WMS
的addWindow
方法。 - 对于
InputChannel
的相关处理调用了WindowState
的openInputChannel
方法。
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
....
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
....
}
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
String name = makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
mInputWindowHandle.inputChannel = inputChannels[0];
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
//`InputChannel`设置到`InputDispatcher`
mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);
}
- 首先调用了
InputChannel
的openInputChannelPair
方法,该方法调用了InputChannel的native
方法nativeOpenInputChannelPair
,创建了两个InputChannel
,对其中一个通过InputManager
进行了InputChannel
的注册。 - 对于
InputChannel
的相关Native
的实现是在InputTransport
中,nativeOpenInputChannelPair
的源码如下
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
outServerChannel.clear();
outClientChannel.clear();
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE;
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
- 注:
Linux
实现了一个源自BSD的socketpair
调用可以实现上述在同一个文件描述符中进行读写的功能(该调用目前也是POSIX规范的一部分 。该系统调用能创建一对已连接的(UNIX族)无名socket
。在Linux中,完全可以把这一对socket
当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写。 -
socketpair
产生的文件描述符是一对socket
,socket
上的标准操作都可以使用,其中也包括shutdown
。——利用shutdown
,可以实现一个半关闭操作,通知对端本进程不再发送数据,同时仍可以利用该文件描述符接收来自对端的数据。 - sendMessage发送消息
status_t InputChannel::sendMessage(const InputMessage* msg) {
size_t msgLength = msg->size();
ssize_t nWrite;
do {
nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
.....
return OK;
}
- 接收消息,通过读socket的方式来读取消息。
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
......
return OK;
}
3 开启输入事件的监听接受事件
- 之前的
setView
中,我们创建了InputChannel
之后,开启了对于InputChannel中输入事件的监听。
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
}
WindowInputEventReceiver
的构造函数如下,其继承自InputEventReceiver
。
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
....
}
-
InputEventReceiver
的构造函数源码如下
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
....
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
}
这里调用了native
方法来做初始化,相关的native
方法的实现在android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
....
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
.....
}
- 根据传入的
InputChannel
和MessageQueue
,创建一个NativeInputEventReceiver
,然后调用其initialize方法。
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
在initialize()
方法中,只调用了一个函数setFdEvents
,
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
- 从
InputConsumer
中获取到channel
的fd,然后调用Looper
的addFd
方法。
int ALooper_addFd(ALooper* looper, int fd, int ident, int events,
ALooper_callbackFunc callback, void* data) {
return ALooper_to_Looper(looper)->addFd(fd, ident, events, callback, data);
}
Looper
的addFd的实现如下
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
Request request;
request.fd = fd;
request.ident = ident;
request.events = events;
request.seq = mNextRequestSeq++;
request.callback = callback;
request.data = data;
if (mNextRequestSeq == -1) mNextRequestSeq = 0;
struct epoll_event eventItem;
request.initEventItem(&eventItem);
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
return -1;
}
mRequests.add(fd, request);
}
}
···
-
注:epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
··· -
该方法所执行的操作就是对传递的fd添加
epoll
监控,Looper
会循环调用pollOnce
方法,而pollOnce
方法的核心实现就是pollInner
。 -
其代码大致实现内容为等待消息的到来,当有消息到来后,根据消息类型做一些判断处理,然后调用其相关的callback。
-
我们当前是对于开启的
socket
的一个监听,当有数据到来,我们便会执行相应的回调。这里对于InputChannel
的回调是在调用了NativeInputEventReceiver
的handleEvent
方法。
4 处理事件 handleEvent--事件读出来
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
return 0; //移除窗口或者IME对话框, 则移除该事件
}
if (events & ALOOPER_EVENT_INPUT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
//【见小节3.3】
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
return status == OK || status == NO_MEMORY ? 1 : 0;
}
if (events & ALOOPER_EVENT_OUTPUT) {
for (size_t i = 0; i < mFinishQueue.size(); i++) {
const Finish& finish = mFinishQueue.itemAt(i);
//【见小节3.4】
status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
if (status) {
mFinishQueue.removeItemsAt(0, i);
if (status == WOULD_BLOCK) {
return 1; //保留callback,稍后重试
}
if (status != DEAD_OBJECT) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
String8 message;
message.appendFormat("Failed to finish input event. status=%d", status);
jniThrowRuntimeException(env, message.string());
mMessageQueue->raiseAndClearException(env, "finishInputEvent");
}
return 0; //移除callback
}
}
mFinishQueue.clear();
setFdEvents(ALOOPER_EVENT_INPUT);
return 1;
}
return 1;
}
- 对于
Event
的处理,这里调用consumeEvents
来对事件进行处理。 - 1.调用consume来进行接受事件
- 执行Java层的InputEventReceiver.dispachInputEvent
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
...
ScopedLocalRef<jobject> receiverObj(env, NULL);
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
//consume 处理(接收事件)
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
if (status) {
if (status == WOULD_BLOCK) {
...
return OK; //消费完成
}
return status; //消失失败
}
if (!skipCallbacks) {
if (!receiverObj.get()) {
receiverObj.reset(jniGetReferent(env, mReceiverWeakGlobal));
if (!receiverObj.get()) {
return DEAD_OBJECT;
}
}
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
//由Native的inputEvent来生成Java层的事件
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
...
}
if (inputEventObj) {
//执行Java层的InputEventReceiver.dispachInputEvent
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
if (env->ExceptionCheck()) {
skipCallbacks = true; //分发过程发生异常
}
env->DeleteLocalRef(inputEventObj);
} else {
skipCallbacks = true;
}
}
if (skipCallbacks) {
//发生异常,则直接向InputDispatcher线程发送完成信号。
mInputConsumer.sendFinishedSignal(seq, false);
}
}
}
status_t InputConsumer::consume(InputEventFactoryInterface* factory,
bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
*outSeq = 0;
*outEvent = NULL;
//循环遍历所有的Event
while (!*outEvent) {
if (mMsgDeferred) {
mMsgDeferred = false; //上一次没有处理的消息
} else {
//收到新消息【见小节3.3.2】
status_t result = mChannel->receiveMessage(&mMsg);
if (result) {
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
break;
}
}
return result;
}
}
switch (mMsg.header.type) {
case InputMessage::TYPE_KEY: {
//从mKeyEventPool池中取出KeyEvent
KeyEvent* keyEvent = factory->createKeyEvent();
if (!keyEvent) return NO_MEMORY;
//将msg封装成KeyEvent
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
break;
}
...
}
}
return OK;
}
- 调用
consume
方法会持续的调用InputChannel
的receiveMessage
方法来从socket
中读取数据。到这里,我们已经将写入socket
的事件读出来了。 -
InputChannel
在创建之后,通过为其InputEventReceiver
对其fd进行epoll
监控,当有变动的时候,调用InputChannel来接收消息。
[-> InputTransport.cpp]
status_t InputChannel::receiveMessage(InputMessage* msg) {
ssize_t nRead;
do {
//读取InputDispatcher发送过来的消息
nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT);
} while (nRead == -1 && errno == EINTR);
if (nRead < 0) {
int error = errno;
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
}
if (error == EPIPE || error == ENOTCONN || error == ECONNREFUSED) {
return DEAD_OBJECT;
}
return -error;
}
if (nRead == 0) {
return DEAD_OBJECT;
}
if (!msg->isValid(nRead)) {
return BAD_VALUE;
}
return OK;
}
5 传递给ViewRootImpl
InputEventReceiver.dispachInputEvent
[-> InputEventReceiver.java]
private void dispatchInputEvent(int seq, InputEvent event) {
mSeqMap.put(event.getSequenceNumber(), seq);
onInputEvent(event);
}
onInputEvent
[-> ViewRootImpl.java ::WindowInputEventReceiver]
final class WindowInputEventReceiver extends InputEventReceiver {
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
...
}
enqueueInputEvent
-
enqueueInputEvent
方法从InputEventReceiver
中获取到InputEvent
,然后将其加入到当前的事件队列之中,最后调用doProcessInputEvents
来进行处理。
void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {
adjustInputEventForCompatibility(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;
if (processImmediately) {
doProcessInputEvents(); //doProcessInputEvents
} else {
scheduleProcessInputEvents();
}
}
doProcessInputEvents
- 遍历所有的消息,如果事件类型为触摸屏事件,对其进行相应的时间修改,最后对于每一个处理完成的事件调用
deliverInputEvent
,
[-> ViewRootImpl.java]
void doProcessInputEvents() {
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
long eventTime = q.mEvent.getEventTimeNano();
long oldestEventTime = eventTime;
...
mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
//[见小节3.3.7]
deliverInputEvent(q);
}
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
事件分发deliverInputEvent
- 首先进行事件的一个判断,通过
shouldSkipIme
来判断是否传递给输入法,然后决定使用何种InputStage
进行消息的继续传递,这里实现了多种InputStage
,对于每一个类型的InputStage
都实现了一个方法process
方法来针对不同类型的事件做处理,如果是触摸屏类的消息,最终会将事件的处理转交到View的身上。
[-> ViewRootImpl.java]
private void deliverInputEvent(QueuedInputEvent q) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q); //[见小节3.4]
}
}
经过一系列的InputStage调用, 最终会分发到真正需要处理该时间的窗口. 当处理完后会调用finishInputEvent(),
总结
至此对于从硬件设备产生数据,到数据被逐层传递到应用程序中的整个流程就梳理完了。事件相关的创建,传递流程如下所示。
4fc1c74f1eadccf675bb560d15223dca.png
input_summary.jpg
6 前传InputChannel
又是如何被设置到InputDispatcher
- Android触摸事件的传递(四)--输入系统-InputReaderThread
-
InputChannel
又是如何被设置到InputDispatcher
之中的呢?在调用openInputChannel
方法,创建Socket
的完成之后,调用该方法。
public void registerInputChannel(InputChannel inputChannel,
InputWindowHandle inputWindowHandle) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel(
env, inputChannel, inputWindowHandle, monitor);
if (status) {
String8 message;
message.appendFormat("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.string());
return;
}
if (! monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
}
-
NativeInputManager
的registerInputChannel
还会调用到InputDispatcher
的registerInputChannel
- 通过
InputChannel
创建相应的Connection
,同时将InputChannel
加入到相应的监控之中 - 在上面对代码的分析之中,获取
InputChannel
,就是通过这个Connection来获取的。
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
if (getConnectionIndexLocked(inputChannel) >= 0) {
return BAD_VALUE;
}
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
- 经过
InputManager
的层层传递,最终会到达InputDispatcher
之中,然后对其进行封装,并在其内部进行保存,同时也传递了相应的窗口的句柄,方便了后期在事件传递的时候,对于窗口的判断。
网友评论