InputChannel
事件通过InputChannel内部的socket进行跨进程通信
今天我们来看一下这个Channel是如何建立的
Input事件最终的接收者一般都是窗口Window
在android系统中一个Activity对应一个ViewRootImpl对象,在Activity启动时会执行handleResumeActivity,这里会创建一个ViewRootImpl对象,并调用其setView函数把Activity的DecorView设置到ViewRootImpl中,而Activity正是在setView函数中注册键盘消息的接收通道的。对于应用程序一定会调用到ViewRootImpl,因此我们从这里开始分析,ViewRootImpl的setView关键代码如下:
base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
//加载布局,保证加载完成前不会收到事件
requestLayout();
//新建一个空的InputChannel
InputChannel inputChannel = null;
inputChannel = new InputChannel();
try {
//通过session跨进程调用到WMS的addWindow方法
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
}
//WindowInputEventReceiver-用于APP端接收Input事件
if (inputChannel != null) {
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
}
...
}
关键方法有3个
- requestLayout是通知IMS这个Activity窗口是当前被激活的窗口,同时将窗口注册到InputDispatcher中。
- mWindowSession.addToDisplay调用WMS的addView方法
- new WindowInputEventReceiver 应用程序这一侧注册消息接收通道
我们看addToDisplay这个方法:
主要就是调用WMS的addView,调用WindowState的openInputChannel,这里的outInputChannel就是上面新建的空InputChannel对象
public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, Rect outFrame,InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
outInputChannel, outInsetsState, outActiveControls, userId);
}
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
...
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], seq, attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
win.openInputChannel(outInputChannel);
...
return res;
}
void openInputChannel(InputChannel outInputChannel) {
String name = getName();
//调用底层方法,生成两个InputChannel对象
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
mInputChannel = inputChannels[0];
mClientChannel = inputChannels[1];
//一个注册到底层InputDispatcher中
mWmService.mInputManager.registerInputChannel(mInputChannel);
//另一个返回给APP端
if (outInputChannel != null) {
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
}
}
这里会通过InputChannel.openInputChannelPair函数来创建一对InputChannel,mInputChannel为server端通过registerInputChannel注册到InputDispatcher中,另外一个mClientChannel为client通过outInputChannel参数返回到APP中,通过WindowInputEventReceiver类在APP端进行监听
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
ScopedUtfChars nameChars(env, nameObj);
std::string name = nameChars.c_str();
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
InputChannel底层通过一对socket进行通信
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
//创建一对无名的、相互连接的套接字
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
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));
sp<IBinder> token = new BBinder();
//socket[0]对应serverFd生成server_channnel
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
//socket[1]对应clientFd生成client_channnel
std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
serverChannel通过java层的registerInputChannel最终调用到InputDispatcher的registerInputChannel方法
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel) {
return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
//生成connection,已fd索引保存在mConnectionsByFd中,此处的fd就是上文中的serverFd
sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
int fd = inputChannel->getFd();
mConnectionsByFd[fd] = connection;
//将fd添加到mLooper的监听上,当有数据时回掉handleReceiveCallback方法
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
}
这里将InputChanel封装成Connection对象,然后socket fd作为key,Connection作为Value,保存在mConnectionsByFd中,同时把fd加入到mLooper的监听中,并指定当该fd有内容可读时,Looper就会调用handleReceiveCallback函数。根据之前事件分发的逻辑,我们知道,当有按键事件发生时,InputDispatcher就会往这些fd写入InputMessage对象
APP端是通过WindowInputEventReceiver来完成对事件的监听的,它继承自InputEventReceiver,将clientChannel注册到底层Looper的监听上
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
//调用底层nativeInit方法,其中looper.getQueue为应用所在进程主线程的消息队列
mInputChannel = inputChannel;
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
}
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();
}
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
//将fd注册到消息队列的监听上,此处的fd为上文的clientFd
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
} else {
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
这里 如果events是0,则表示要移除监听fd,如果events不为0,表示要监听fd,这个fd就是前面WMS创建的一对InputChannel的client端,当server端写入事件时,client端的looper就能被唤醒,并调用handleEvent函数(当fd可读时,会调用LooperCallback的handleEvent,而NativeInputEventReceiver继承自LooperCallback,所以这里会调用NativeInputEventReceiver的handleEvent函数),这里分析NativeInputEventReceiver.handleEvent,代码实现很简单,若是输入事件就通过consumeEvents方法消费掉这个事件
如下:
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
if (events & ALOOPER_EVENT_INPUT) {
status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
}
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
bool skipCallbacks = false;
for (;;) {
uint32_t seq;
InputEvent* inputEvent;
//mInputConsumer.consume读取事件
status_t status = mInputConsumer.consume(&mInputEventFactory,
consumeBatches, frameTime, &seq, &inputEvent);
jobject inputEventObj;
switch (inputEvent->getType()) {
case AINPUT_EVENT_TYPE_KEY:
inputEventObj = android_view_KeyEvent_fromNative(env,
static_cast<KeyEvent*>(inputEvent));
break;
//Native Callback回调到Java层的dispatchInputEvent方法
env->CallVoidMethod(receiverObj.get(),
gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
}
}
先通过InputChannel的receiveMessage函数接收InputMessage,根据InputMessage对象调用initializeKeyEvent来构造KeyEvent对象。这里便拿到了cpp层的KeyEvent对象,再通过android_view_KeyEvent_fromNative转化成java层的KeyEvent对象,通过Native Callback回调到InputEventReceiver.dispatchInputEvent方法中,接下来输入事件就到了java层的分发。致此,Dispatcher线程与WMS,以及APP之间建立联系的过程分析到此结束
InputMessage mMsg;
status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
while (!*outEvent) {
//通过InputChannel的receiveMessage函数接收InputMessage
status_t result = mChannel->receiveMessage(&mMsg);
switch (mMsg.header.type) {
case InputMessage::Type::KEY: {
KeyEvent* keyEvent = factory->createKeyEvent();
//initializeKeyEvent来构造KeyEvent
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
break;
}
}
}
return OK;
}
// Called from native code.
private void dispatchInputEvent(int seq, InputEvent event) {
onInputEvent(event);
}
InputChannel的底层实现创建的socket是一对,是能双向传输的通道,serverFd给到了底层InputDispatcher用于分发事件,clientFd给到了APP侧用于监听,所以有发送当然就可以有返回。
APP端接收处理完事件后,会通过同样的方式写入到clientFd给serverFd发送数据,触发InputDispatcher的handleReceiveCallback方法,来完成事件的后续清理工作,这个在之前已经分析过。