Input机制之-InputChannel建立连接

本文详细介绍了Android系统中InputChannel如何建立并用于跨进程通信,涉及Activity、ViewRootImpl、WindowSession、WMS、InputDispatcher等组件的角色。InputChannel通过一对socket进行通信,服务器端注册到InputDispatcher,客户端在APP端通过WindowInputEventReceiver监听事件。当事件发生时,InputDispatcher将事件写入socket,唤醒APP端的Looper进行处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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方法,来完成事件的后续清理工作,这个在之前已经分析过。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值