由浅入深学习android input系统 - InputChannel解析

Posted 许佳佳233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了由浅入深学习android input系统 - InputChannel解析相关的知识,希望对你有一定的参考价值。

概述

前文链接:由浅入深学习android input系统(二) - input事件如何传递到app进程( InputDispatcher )

根据前文分析,InputDispatcher将事件分发给app进程是通过InputChannel,那么InputChannel又是什么时候建立的呢?

InputChannel的初始化

InputChannel初始化,调用任务栈如下:

  • ViewRootImpl.setView()
  • IWindowSession.addToDisplay()
    等同于 WindowManagerService.addToDisplay()
  • WindowManagerService.addWindow()
  • WindowState.openInputChannel()
  • android.view.InputChannel.openInputChannelPair()
  • android.view.InputChannel.nativeOpenInputChannelPair()
  • android_view_InputChannel_nativeOpenInputChannelPair()
  • InputTransport.InputChannel.openInputChannelPair()

InputTransport.InputChannel.openInputChannelPair()

此处分别初始化了两个socket,一个是client,一个是server,两个socket分别都支持接收与发送。
那么client和server分别对应什么场景呢?这需要继续根据任务栈往上看。

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)) {
        status_t result = -errno;
        ALOGE("channel '%s' ~ Could not create socket pair.  errno=%d",
                name.c_str(), 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));

    std::string serverChannelName = name;
    serverChannelName += " (server)";
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    std::string clientChannelName = name;
    clientChannelName += " (client)";
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

serverChannel与clientChannel

android_view_InputChannel_nativeOpenInputChannelPair

此处会根据创建的client与server的两个channel分别创建两个android.view.InputChannel。
最终返回javaArray对象,0的位置是serverChannel,1的位置是clientChannel。

于是继续根据调用任务栈往上看。

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
        jclass clazz, jstring nameObj) {
    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
    std::string name = nameChars;
    env->ReleaseStringUTFChars(nameObj, nameChars);

    sp<InputChannel> serverChannel;
    sp<InputChannel> clientChannel;
    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);

    if (result) {
        String8 message;
        message.appendFormat("Could not open input channel pair.  status=%d", result);
        jniThrowRuntimeException(env, message.string());
        return NULL;
    }

    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;
}

int register_android_view_InputChannel(JNIEnv* env) {
    int res = RegisterMethodsOrDie(env, "android/view/InputChannel", gInputChannelMethods,
                                   NELEM(gInputChannelMethods));

    jclass clazz = FindClassOrDie(env, "android/view/InputChannel");
    gInputChannelClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);

    gInputChannelClassInfo.mPtr = GetFieldIDOrDie(env, gInputChannelClassInfo.clazz, "mPtr", "J");

    gInputChannelClassInfo.ctor = GetMethodIDOrDie(env, gInputChannelClassInfo.clazz, "<init>",
                                                   "()V");

    return res;
}

WindowState.openInputChannel()

逻辑如下:

  1. mInputChannel指向serverChannel对象,mClientChannel指向clientChannel对象。
  2. InputManagerService.registerInputChannel()方法会将mInputChannel注册进去。(后面具体分析)
  3. 通过transferTo()方法,让outInputChannel使用mClientChannel的通道,然后回收mClientChannel的相关资源。

那么接下来其实我们就有两个探索的路线了:

  • serverChannel:需要探索InputManagerService.registerInputChannel()
  • clientChannel:需要探索outInputChannel是什么
    void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
            throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName();
        InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0];
        mClientChannel = inputChannels[1];
        mWmService.mInputManager.registerInputChannel(mInputChannel);
        mInputWindowHandle.token = mInputChannel.getToken();
        if (outInputChannel != null) {
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            // If the window died visible, we setup a dummy input channel, so that taps
            // can still detected by input monitor channel, and we can relaunch the app.
            // Create dummy event receiver that simply reports all events as handled.
            mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
        }
        mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
    }

serverChannel的探索

根据InputManagerService.registerInputChannel(),可以得到如下的任务栈:

  • InputManagerService.registerInputChannel()
  • InputManagerService.nativeRegisterInputChannel()
  • NativeInputManager.registerInputChannel()
  • InputDispatcher.registerInputChannel()

由此可以得知,这里的serverChannel就是InputDispatcher对应的InputChannel。
InputDispatcher通过这个channel将事件分发给具体的window。

对InputDispatcher不是很熟悉的读者推荐看下前文:

clientChannel的探索

再罗列下InputChannel初始化的任务栈:

  • ViewRootImpl.setView()
  • IWindowSession.addToDisplay()
    等同于 WindowManagerService.addToDisplay()
  • WindowManagerService.addWindow()
  • WindowState.openInputChannel()

通过上面这个任务栈回溯,我们可以发现,最终这个Channel是在ViewRootImpl.setView()中创建的。
并且在创建之后也没有初始化过,最终其实就是使用了clientChannel的通道。

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    ——————————————————省略
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                mForceDecorViewVisibility = (mWindowAttributes.privateFlags
                        & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } 
    ——————————————————省略
    }


这个channel会在哪里使用到?

  • 每个widnow都会使用这个channel来监听系统进程发来的事件
  • 由于clientChannel是在WindowState中通过openInputChannel()方法来初始化的,因此,每个window都对应一个clientChannel。
  • 也就意味着每个window都对应一个接收事件的socket通道,一个app进程如果有多个window,那么也会有多个接收事件的socket通道。

对inputChannel作用不是很了解的读者,推荐看下前两篇文章:

总结

  • 每个window会对应两个InputChannel,serverChannel与clientChannel。
  • serverChannel会注册到InputDispatcher中,InputDispatcher会通过这个InputChannel将事件分发给对应的进程与window。这个事件是clientChannel来接收。
  • clientChannel负责接收InputDispatcher发来的事件,然后将事件传递个ViewRootImpl,再传递给View。
  • InputChannel初始化的时机是:WindowManagerService.addWindow(),也就是window第一次被注册到系统进程的时候。

以上是关于由浅入深学习android input系统 - InputChannel解析的主要内容,如果未能解决你的问题,请参考以下文章

由浅入深学习android input系统 - input系统的启动

由浅入深学习android input系统 - input事件采集(InputReader)

由浅入深学习android input系统 - input事件采集(InputReader)

由浅入深学习android input系统 - input事件如何传递到View

由浅入深学习android input系统 - input事件如何传递到View

由浅入深学习android input系统 - InputChannel解析