应用程序注册输入事件通道

来源:互联网 发布:软件技术服务协议范本 编辑:程序博客网 时间:2024/06/08 06:08

应用程序注册输入事件通道

    • 应用程序注册输入事件通道
      • 简介
      • 源码分析
        • 通过requestLayout函数更新InputManagerService中激活的Activity窗口
        • 把Server端输入事件通道注册到InputDispatcher的消息循环中
        • 把Client输入事件通道注册到应用程序的消息循环中
      • 总结

1.简介

InputManagerService服务启动以后,就要开始监控输入事件了。当InputManagerService监控到输入事件时,它会把这个输入事件消息分发给当前激活的Activity窗口。在分发到激活Activity之前,还需要当前激活的Activity窗口先注册一个输入事件消息接收通道到InputManagerService中去,这样InputManagerService才能把输入事件消息分发给它处理。

那么,当前被激活的Activity窗口是在什么时候去注册这个输入事件接收通道呢?我们知道在Activity的启动流程中,当执行完ActivityThread的handleLaunchActivity函数后,会接着执行handleResumeActivity的流程,使Activity处于Resumed状态。在调用handleResumeActivity的过程中,会创建一个ViewRootImpl对象,并调用ViewRootImpl的setView方法把Activity相关的View设置到这个ViewRootImpl中去。在ViewRootImpl的setView过程中,会注册输入事件消息接收通道。

接下来将从源码的角度来分析输入事件消息接收通道的注册过程。

2.源码分析

2.1 ActivityThread.handleResumeActivity()

final void handleResumeActivity(IBinder token,        boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {    ......    if (r.window == null && !a.mFinished && willBeVisible) {            r.window = r.activity.getWindow();            View decor = r.window.getDecorView();            decor.setVisibility(View.INVISIBLE);            ViewManager wm = a.getWindowManager();            WindowManager.LayoutParams l = r.window.getAttributes();            a.mDecor = decor;            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;            l.softInputMode |= forwardBit;            ......            if (a.mVisibleFromClient && !a.mWindowAdded) {                a.mWindowAdded = true;                wm.addView(decor, l);// 调用WindowManagerImpl的addView方法,见2.2            }        } else if (!willBeVisible) {            ......        }}

在handleResumeActivity()方法中,会调用WindowManagerImpl的addView()将Activity的mDecor视图添加到WindowManagerService中去。

2.2 WindowManagerGlobal.addView()

public void addView(View view, ViewGroup.LayoutParams params,   Display display, Window parentWindow) {    .......    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;    ......    ViewRootImpl root;    View panelParentView = null;    .......    root = new ViewRootImpl(view.getContext(), display);// 创建一个ViewRootImpl对象    view.setLayoutParams(wparams);//给View设置参数    .......    // 给View、ViewRootImpl以及params添加到集合中去    mViews.add(view);    mRoots.add(root);    mParams.add(wparams);    try {            root.setView(view, wparams, panelParentView);// 调用ViewRootImpl的setView方法,见2.3    }catch(RuntimeException e){        if (index >= 0) {                removeViewLocked(index, true);            }        throw e;    }}

在WindowManagerGlobal的addView方法中,主要是创建一个ViewRootImpl对象,并把对应的View、ViewRootImpl以及param参数分别保存到集合中,最后调用ViewRootImpl的setView()方法。

2.3 ViewRootImpl.setView()

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {    synchronized (this) {        if (mView == null) {            mView = view;            .......            requestLayout();// 1.请求布局,通知InputManagerService当前Activity窗口是激活的,见2.4            if ((mWindowAttributes.inputFeatures                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {                mInputChannel = new InputChannel();//创建InputChannel通道            }            .......            try {                .......                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                        getHostVisibility(), mDisplay.getDisplayId(),                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                        mAttachInfo.mOutsets, mInputChannel);// 2.把Server端输入事件通道注册到InputDispatcher的消息循环中,见2.13            } catch (RemoteException e) {                ......            }            .....            if (mInputChannel != null) {                if (mInputQueueCallback != null) {                    mInputQueue = new InputQueue();                    mInputQueueCallback.onInputQueueCreated(mInputQueue);                }                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,                        Looper.myLooper());//3.把Client输入事件通道注册到应用程序的消息循环中,见2.22            }            ......        }    }}

在ViewRootImpl的setView过程中,首先调用requestLayout函数来通知InputManagerService,这个Activity窗口是当前被激活的窗口。接着,调用sWindowSession(WindowManagerService内部类Session的远程接口)的add成员函数来把输入事件消息接收通道的一端注册在InputManagerService中。最后是创建一个WindowInputEventReceiver对象实例,把输入事件接收通道的另一端注册在本应用程序的消息循环(Looper)中。这样当InputManagerService监控到有输入事件消息时,就会先找到当前被激活的窗口,然后找到其在InputManagerService中对应的输入事件消息接收通道,通过这个通道在InputManagerService中的一端来通知在应用程序消息循环中的另一端,就这样输入事件消息分发给当前激活的Activity窗口了。

接下来将分为三部分来描述输入消息接收通道注册过程:

  1. 通过requestLayout函数更新InputManagerService中激活的Activity窗口;
  2. 把Server端输入事件通道注册到InputDispatcher的消息循环中;
  3. 把Client输入事件通道注册到应用程序的消息循环中;

通过requestLayout函数更新InputManagerService中激活的Activity窗口

2.4 ViewRootImpl.scheduleTraversals()

void scheduleTraversals() {    if (!mTraversalScheduled) {        mTraversalScheduled = true;        .......        mChoreographer.postCallback(                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);        ......    }}final TraversalRunnable mTraversalRunnable = new TraversalRunnable();final class TraversalRunnable implements Runnable {    @Override    public void run() {        doTraversal();    }}void doTraversal() {    if (mTraversalScheduled) {        mTraversalScheduled = false;        ......        performTraversals();        .....    }}

可以看到scheduleTraversals()函数经过层层调用,最终会调用到performTraversals()函数。

private void performTraversals() {    ......    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);    .....}

在performTraversals方法中,会调用relayoutWindow()方法进一步处理,最终调用到mWindowSession的relayout方法。

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,        boolean insetsPending) throws RemoteException {    ......    int relayoutResult = mWindowSession.relayout(            mWindow, mSeq, params,            (int) (mView.getMeasuredWidth() * appScale + 0.5f),            (int) (mView.getMeasuredHeight() * appScale + 0.5f),            viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,            mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,            mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,            mSurface);//调用WindowSession的热layout方法,见2.5    ......}

mWindowSession对象在构建ViewRootImpl时创建的,它是WindowManagerService内部类Session的远程接口,通过该接口可以调用到WindowManagerService中的方法。

2.5 Session.relayout()

final class Session extends IWindowSession.Stub{    ......    public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,        int requestedWidth, int requestedHeight, int viewFlags,        int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,        Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,        Configuration outConfig, Surface outSurface) {    ......    int res = mService.relayoutWindow(this, window, seq, attrs,            requestedWidth, requestedHeight, viewFlags, flags,            outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,            outStableInsets, outsets, outBackdropFrame, outConfig, outSurface);//调用WindowManagerService的relayoutWindow方法,见2.6    ......    return res;    }    ......}

在构建Session时,会初始化mService变量,该变量是WindowManagerService,接着会调用WindowManagerService的relayoutWindow方法。

2.6 WindowManagerService.relayoutWindow()

    public int relayoutWindow(Session session, IWindow client, int seq,        WindowManager.LayoutParams attrs, int requestedWidth,        int requestedHeight, int viewVisibility, int flags,        Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,        Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,        Configuration outConfig, Surface outSurface) {        ......        mInputMonitor.updateInputWindowsLw(true /*force*/);// 调用InputMonitor的updateInputWindowsLw方法,见2.7        ......    }

该方法的处理逻辑有点长,与更新当前输入激活Activity窗口相关的方法是InputMonitor的updateInputWindowsLw()方法。

2.7 InputMonitor.updateInputWindowsLw()

    /* Updates the cached window information provided to the input dispatcher. */public void updateInputWindowsLw(boolean force) {    .....    mUpdateInputWindowsNeeded = false;    // Add all windows on the default display.    final int numDisplays = mService.mDisplayContents.size();//如果存在多个屏幕的话,获取屏幕的数量    // 逐个遍历屏幕,处理屏幕上的所有窗口    for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {        final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx);        final WindowList windows = displayContent.getWindowList();        for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {            final WindowState child = windows.get(winNdx);//获取窗口的状态            final InputChannel inputChannel = child.mInputChannel;//获取输入通道            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;//获取输入通道handle            ......              // 获取窗口的一些状态信息,包括flags,type以及hasFoucus            final int flags = child.mAttrs.flags;            final int privateFlags = child.mAttrs.privateFlags;            final int type = child.mAttrs.type;            final boolean hasFocus = (child == mInputFocus);            final boolean isVisible = child.isVisibleLw();            final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);// 是否为默认的显示屏幕            addInputWindowHandleLw(                    inputWindowHandle, child, flags, type, isVisible, hasFocus, hasWallpaper);// 更新InputWindowHandle,并把InputWindowHandle存放到mInputWindowHandles数组中,见2.8        }        ......        // Send windows to native code.        mService.mInputManager.setInputWindows(mInputWindowHandles);//将当前屏幕上的窗口发送到本地代码,见2.9        .....    }}

在updateInputWindowsLw()方法中,会去遍历屏幕上的所有窗口,并把窗口的一些状态信息,例如是否获取了焦点,是否可见等信息更新到InputWindowHandle中,并把InputWindowHandle添加到mInputWindowHandles集合中,并把这些窗口集合发送到native代码中。

2.8 InputMonitor.addInputWindowHandleLw()

private void addInputWindowHandleLw(final InputWindowHandle inputWindowHandle,        final WindowState child, int flags, final int type, final boolean isVisible,        final boolean hasFocus, final boolean hasWallpaper) {    // 更新InputWindowHandle各个参数的值    inputWindowHandle.name = child.toString();    flags = child.getTouchableRegion(inputWindowHandle.touchableRegion, flags);    inputWindowHandle.layoutParamsFlags = flags;    inputWindowHandle.layoutParamsType = type;    inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();    inputWindowHandle.visible = isVisible;    inputWindowHandle.canReceiveKeys = child.canReceiveKeys();    inputWindowHandle.hasFocus = hasFocus;    inputWindowHandle.hasWallpaper = hasWallpaper;    inputWindowHandle.paused = child.mAppToken != null ? child.mAppToken.paused : false;    inputWindowHandle.layer = child.mLayer;    inputWindowHandle.ownerPid = child.mSession.mPid;    inputWindowHandle.ownerUid = child.mSession.mUid;    inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;    final Rect frame = child.mFrame;    inputWindowHandle.frameLeft = frame.left;    inputWindowHandle.frameTop = frame.top;    inputWindowHandle.frameRight = frame.right;    inputWindowHandle.frameBottom = frame.bottom;    ......     addInputWindowHandleLw(inputWindowHandle);}private void addInputWindowHandleLw(final InputWindowHandle windowHandle) {    if (mInputWindowHandles == null) {        mInputWindowHandles = new InputWindowHandle[16];// 数组大小为16个    }    if (mInputWindowHandleCount >= mInputWindowHandles.length) {        mInputWindowHandles = Arrays.copyOf(mInputWindowHandles,                mInputWindowHandleCount * 2);// 扩容    }    mInputWindowHandles[mInputWindowHandleCount++] = windowHandle;//将InputWindowHandle保存到数组中}

可以看到,addInputWindowHandleLw函数主要是更新InputWindowHandle的值,并把它添加到mInputWindowHandles数组中。

2.9 InputManagerService.setInputWindows()

public void setInputWindows(InputWindowHandle[] windowHandles) {    nativeSetInputWindows(mPtr, windowHandles);//调用native方法,见2.10}

2.10 com_android_server_input_InputManagerService.nativeSetInputWindows()

static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobjectArray windowHandleObjArray) {
NativeInputManager* im = reinterpret_cast

把Server端输入事件通道注册到InputDispatcher的消息循环中

ViewRootImpl的setView()方法中,调用完了requestLayout后,接着调用mWindowSession的addToDisplay()方法。

2.13 Session.addToDisplay()

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,        int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,        Rect outOutsets, InputChannel outInputChannel) {    return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,            outContentInsets, outStableInsets, outOutsets, outInputChannel);// 见2.14}

2.14 WindowManagerService.addWindow()

 public int addWindow(Session session, IWindow client, int seq,        WindowManager.LayoutParams attrs, int viewVisibility, int displayId,        Rect outContentInsets, Rect outStableInsets, Rect outOutsets,        InputChannel outInputChannel) {    ......    WindowState win = new WindowState(this, session, client, token,                attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);// 创建WindowState对象    ......    final boolean openInputChannels = (outInputChannel != null                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);        if  (openInputChannels) {            win.openInputChannel(outInputChannel);// 调用WindowState的openInputChannel,打开输入通道,见2.15        }    ......}

在该方法中,主要是通过WindowState的openInputChannel方法来打开输入通道。

2.15 WindowState.openInputChannel()

void openInputChannel(InputChannel outInputChannel) {    // 如果窗口已经有输入通道了,则抛出异常    if (mInputChannel != null) {        throw new IllegalStateException("Window already has an input channel.");    }    String name = makeInputChannelName();//获取输入通道名字    InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);//创建一对输入通道,见2.16    mInputChannel = inputChannels[0];//保存在WindowState的mInputChannel变量中    mClientChannel = inputChannels[1];    mInputWindowHandle.inputChannel = inputChannels[0];    if (outInputChannel != null) {        mClientChannel.transferTo(outInputChannel);        mClientChannel.dispose();        mClientChannel = null;    } else {        mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);    }    // 注册服务端输入通道,见2.19    mService.mInputManager.registerInputChannel(mInputChannel, mInputWindowHandle);}

通过InputChannel的openInputChannelPair()方法将创建一对输入通道,其中一个保存在WindowState的mInputChannel中,提供给InputDispatcher使用;另外一个通过outInputChannel参数返回到应用程序,提供给应用程序的输入队列使用。

InputChannel的transferTo()方法的主要作用是转移输入通道的所属关系,这样就可以把输入通道保存到应用程序中。

2.16 InputChannel.openInputChannelPair()

public static InputChannel[] openInputChannelPair(String name) {    if (name == null) {        throw new IllegalArgumentException("name must not be null");    }    return nativeOpenInputChannelPair(name);// 见2.17}

该方法的主要作用是创建一对输入通道,一个给InputDispatcher使用,另外一个给应用程序的输入队列使用;

2.17 nativeOpenInputChannelPair()

static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,    jclass clazz, jstring nameObj) {    const char* nameChars = env->GetStringUTFChars(nameObj, NULL);// 获取管道的名字    String8 name(nameChars);    env->ReleaseStringUTFChars(nameObj, nameChars);    sp<InputChannel> serverChannel;//服务端的通道    sp<InputChannel> clientChannel;//客户端的通道    status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);//创建输入通道,见2.18    // 创建失败    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);//创建一个数组,大小为2    if (env->ExceptionCheck()) {        return NULL;    }    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,            std::make_unique<NativeInputChannel>(serverChannel));//创建Java层服务端输入通道    if (env->ExceptionCheck()) {        return NULL;    }    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,            std::make_unique<NativeInputChannel>(clientChannel));//创建Java层客户端输入通道    if (env->ExceptionCheck()) {        return NULL;    }    //将Java层的服务端输入通道和客户端输入通道分别保存在channelPair数组中,并返回到Java层。    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);    return channelPair;}

根据传递进来的参数name,创建两个native层的输入通道,一个给服务端server使用,另外一个给client端使用;这里的server端是指InputDispatcher,而Client端是指应用程序。

创建Native层的输入通道是通过InputChannel的openInputChannelPair方法。

2.18 InputChannel.openInputChannelPair()

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

在InputChannel的openInputChannelPair()方法中,首先通过socketpair()方法打开套接字对,然后设置套接字的缓存大小,最后分别创建服务端和客户端的输入通道。

socketpair()函数建立一对匿名的已经连接的套接字,可以实现在同一个文件描述符中进行读写的功能。在Linux中,完全可以把这一对socket当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写。与一般的管道相区别的是,套接字对建立的通道是双向的,即每一端都可以进行读写。

我们知道套接字可以用于进程间通信,这里用两个套接字,分别用于服务端输入通道和客户端输入通道。

Native层Channel的创建过程如下:

InputChannel::InputChannel(const String8& name, int fd) :    mName(name), mFd(fd) {//初始化名字和文件描述符    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);// 设置为非阻塞    LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket "        "non-blocking.  errno=%d", mName.string(), errno);}

创建完输入通道后,需要返回到2.15中,接着调用InputManagerService的registerInputChannel注册服务端输入通道。

2.19 InputManagerService.registerInputChannel()

public void registerInputChannel(InputChannel inputChannel,        InputWindowHandle inputWindowHandle) {    if (inputChannel == null) {        throw new IllegalArgumentException("inputChannel must not be null.");    }    nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);// 见2.20}

该方法的主要作用是注册一个输入通道,这样它可以用来接收输入事件消息。该方法有两个参数,一个是InputChannel,代表需要注册的输入通道;另外一个是inputWindowHandle,代表输入通道需要关联的输入窗口。

2.20 nativeRegisterInputChannel()

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);// 获取native层的输入通道    if (inputChannel == NULL) {        throwInputChannelNotInitialized(env);        return;    }    sp<InputWindowHandle> inputWindowHandle =            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);// 获取native层的InputWindowHandle    status_t status = im->registerInputChannel(            env, inputChannel, inputWindowHandle, monitor);// 调用InputDispatcher的注册输入通道方法,见2.21    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);    }}

在该方法中,首先获取native层的输入通道inputChannel和InputWindowHandle,然后调用InputDispatcher的registerInputChannel()注册输入通道方法。

2.21 InputDispatcher.registerInputChannel()

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,    const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {    { // acquire lock        AutoMutex _l(mLock);        // 如果已经注册该输入通道,则直接返回        if (getConnectionIndexLocked(inputChannel) >= 0) {            ALOGW("Attempted to register already registered input channel '%s'",                inputChannel->getName().string());            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);// 通过Looper的addFd方法,将文件描述符添加到Looper的监控中,并设置回调处理函数为handleReceiveCallback方法。    } // release lock    // Wake the looper because some connections have changed.    mLooper->wake();//唤醒操作    return OK;}

在该方法中,首先检查该输入通道是否已经注册过了,如果已经注册了,则直接返回。如果没有注册过,则首先创建一个连接,并保存在以文件描述符为key的Map集合中。该文件描述符是之前2.18中创建的套接字文件描述符。

最后调用Looper的addFd方法来将套接字文件描述符添加到Looper的监控范围内,并设置回调处理函数handleReceiveCallback。Looper通过IO多路复用机制epoll可以同时监控多个文件描述符,如果其中有文件描述符准备好了,则唤醒Looper线程进行处理,并调用相应的回调函数处理。

至此,在服务端server的输入通道注册已经完成了,主要是调用InputChannel的openInputChannelPair()方法创建两个输入通道,一个给server端InputDispatcher使用,另外一个给应用程序Client端使用;接着调用InputDispatcher的registerInputChannel方法,将服务端输入通道进行注册,最后是通过Looper的addFd方法将输入通道文件描述符添加到监控范围内,等待输入通道数据。

下面来分析第三部分,把Client输入事件通道注册到应用程序的消息循环中。

把Client输入事件通道注册到应用程序的消息循环中

前面完成了Server端输入事件通道注册到InputDispatcher的消息循环中,接着回到2.3中,在函数的最后,会调用根据mInputChannel创建一个WindowInputEventReceiver对象。mInputChannel在2.15中与native层的输入信道进行关联了。具体是通过mClientChannel.transferTo(outInputChannel)方法进行关联的。

2.22 WindowInputEventReceiver()

final class WindowInputEventReceiver extends InputEventReceiver {    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {        super(inputChannel, looper);//见2.23    }    ......}

WindowInputEventReceiver继承自InputEventReceiver,最终会调用到InputEventReceiver的构造方法。

2.23 InputEventReceiver()

public InputEventReceiver(InputChannel inputChannel, Looper looper) {    if (inputChannel == null) {        throw new IllegalArgumentException("inputChannel must not be null");    }    if (looper == null) {        throw new IllegalArgumentException("looper must not be null");    }    mInputChannel = inputChannel;    mMessageQueue = looper.getQueue();// 获取Looper的信息队列    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),            inputChannel, mMessageQueue);// 调用nativeInit方法初始化,见2.24    mCloseGuard.open("dispose");}

2.24 android_view_InputEventReceiver.nativeInit()

static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,    jobject inputChannelObj, jobject messageQueueObj) {    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,            inputChannelObj);// 获取native层的输入通道    if (inputChannel == NULL) {        jniThrowRuntimeException(env, "InputChannel is not initialized.");        return 0;    }    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);// 获取native层的信息队列    if (messageQueue == NULL) {        jniThrowRuntimeException(env, "MessageQueue is not initialized.");        return 0;    }    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,            receiverWeak, inputChannel, messageQueue);//创建一个NativeInputEventReceiver对象,见2.25    status_t status = receiver->initialize();//执行一些初始化操作,见2.26    if (status) {        String8 message;        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);        jniThrowRuntimeException(env, message.string());        return 0;    }    receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object    return reinterpret_cast<jlong>(receiver.get());}

在InputEventReceiver的nativeInit()方法中,主要是创建一个native层的NativeInputEventReceiver,并执行一些初始化操作。

2.25 NativeInputEventReceiver()

NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,    jobject receiverWeak, const sp<InputChannel>& inputChannel,    const sp<MessageQueue>& messageQueue) :    mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),    mInputConsumer(inputChannel), mMessageQueue(messageQueue),    mBatchedInputEventPending(false), mFdEvents(0) {    ......}

在创建NativeInputEventReceiver时,主要是初始化输入通道,消息队列以及文件描述符支持的操作。

2.26 NativeInputEventReceiver.initialize()

status_t NativeInputEventReceiver::initialize() {    setFdEvents(ALOOPER_EVENT_INPUT);//见2.27    return OK;}

ALOOPER_EVENT_INPUT表示文件描述符允许可读操作

2.27 NativeInputEventReceiver.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);//将文件描述符添加到消息队列关联Looper循环中,监听events事件,并设置事件回调处理对象为NativeInputEventReceiver。        } else {            mMessageQueue->getLooper()->removeFd(fd);        }    }}

在setFdEvents方法中,主要是将文件描述符添加到消息队列MessageQueue关联的Looper循环中,Looper循环通过IO多路复用机制Epoll可以监控多个文件描述符fd上的event事件,并设置事件回调处理对象为NativeInputEventReceiver。
当文件描述符fd上的事件events发生了,则唤醒Looper执行相应的事件回调处理对象NativeInputEventReceiver处理。

至此,应用程序端的输入通道注册已经完成了,其主要是通过应用程序消息队列MessageQueue关联的Looper对象,调用Looper.addFd()方法,将输入通道文件描述符添加到Looper循环的监控范围内,等待输入事件的到来。

3.总结

WindowManagerGlobal.addView()    ViewRootImpl.setView()        ViewRootImpl.requestLayout()            WindowManagerService.relayoutWindow()                InputDispatcher.setInputWindows()        Session.addToDisplay()            WindowManagerService.addWindow()                WindowState.openInputChannel()                    InputChannel.openInputChannelPair() -------> 创建socketpair                InputManagerService.registerInputChannel()                    InputDispatcher.registerInputChannel() ------->  注册server端sockets[0]            WindowInputEventReceiver()                InputEventReceiver()                    NativeInputEventReceiver.initialize()                        NativeInputEventReceiver.setFdEvents() ------->  注册client端sockets[1]

应用程序注册输入消息接收通道主要分为三个部分:

  • 第一部分是通过requestLayout()方法,将当前处于激活状态的Activity窗口更新到InputDispatcher中;
  • 第二部分是将server端InputChannel关联的套接字sockets[0]添加到InputDispatcher线程的监听中,当收到消息时,回调InputDispatcher的handleReceiveCallback()方法进行处理;
  • 第三部分是将client端InputChannel关联的套接字sockets[1]添加到应用程序的UI线程的监听中,当收到消息时,回调NativeInputEventReceiver.handleEvent()方法进行处理;

server端和client端输入通道的注册,都是通过调用Looper.addFd()方法,将输入通道关联的文件描述符添加Looper的监控范围内。当输入通道文件描述符有数据可读时,唤醒相应的回调处理函数处理。

原创粉丝点击