Android Input系统5 UI线程

一 概述

前面介绍了 InputReader 和 InputDispatcher 两个线程的工作过程。在 InputDispatcher 的工作过程中讲到调用 InputChanel 通过 socket 与远程进程通信,本文便展开讲解这个 socket 是如何建立的。

对于 InputReader 和 InputDispatcher 都是运行在 system_server 进程; 用户点击的界面,往往可能是在某一个 app 中,而每个 app 一般都运行在自己的进程里,这里就涉及到跨进程通信,也就是 app 进程是如何与 system_server 进程建立通信的?答案就是 InputChannel,我们看看 InputChannel 的注释:

/**
 * An input channel specifies the file descriptors used to send input events to
 * a window in another process.  It is Parcelable so that it can be sent
 * to the process that is to receive events.  Only one thread should be reading
 * from an InputChannel at a time.
 * @hide
 */
public final class InputChannel implements Parcelable {
	.....
}

说它是用来将输入事件发送到其他进程的窗口的一个通道,并且它实现了 Parcelable,可以将其发送到需要接收输入事件的进程,从注释中我们就能清楚地知道 InputChannel 的作用。

要了解 InputChannel 是何时建立起来的,需要从 Activity 最基本的创建过程开始说起。我们都知道,一般地一个 Activity 对应一个应用窗口,每一个窗口对应一个 ViewRootImpl。窗口是如何添加到 Activity 的,一切需要从 Activity.onCreate() 为起点讲解。

二 UI线程

我们已经知道,Activity 生命周期的回调方法,都是运行在主线程的,主线程也称之为 UI 线程,所有 UI 相关的操作都需要运行在该线程。本文题目虽然是 UI 线程,但并非只介绍所有运行在 UI 线程的流程,文中还涉及 binder thread。

2.1 Activity.onCreate

Activity.java

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ......
}

Activity 的启动是由 system_server 进程控制的:(参考 Android四大组件系列2 Activity启动流程(上)

  • handleLaunchActivity():会调用 Activity.onCreate(),该方法内再调用 setContentView(),经过 AMS 与 WMS 的各种交互,层层调用后,进入 handleResumeActivity()
  • handleResumeActivity():会调用 Activity.makeVisible(),该方法继续调用,便会执行到 WindowManagerImpl.addView(),该方法内部再调用 WindowManagerGlobal.addView()

2.2 WindowManagerGlobal.addView

WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
    ......
    ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
    root.setView(view, wparams, panelParentView);
    ......
}

2.3 ViewRootImpl

ViewRootImpl.java

public ViewRootImpl(Context context, Display display) {
    mContext = context;
    // 获取 IWindowSession 的代理类,为单例模式
    mWindowSession = WindowManagerGlobal.getWindowSession();
    mDisplay = display;
    mThread = Thread.currentThread(); // 主线程
    // IWindow.Stub 的实现类,用于服务端向客户端通信
    mWindow = new W(this);
    mChoreographer = Choreographer.getInstance();
    ......
}

2.3.1 WindowManagerGlobal.getWindowSession

WindowManagerGlobal.java

public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                // 获取 WMS 的代理类
                IWindowManager windowManager = getWindowManagerService();
                // 经过 Binder 调用,最终调用 WMS
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
            } catch (RemoteException e) {
                ...
            }
        }
        return sWindowSession
    }
}

2.3.2 WMS.openSession

public IWindowSession openSession(IWindowSessionCallback callback) {
        return new Session(this, callback);
}

再次经过 Binder 将数据返回给 app 进程,则获取的便是 Session 的代理对象。

2.3.3 ViewRootImpl.setView

ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs,
        View panelParentView) {
    synchronized (this) {
      ......
      requestLayout();
      if ((mWindowAttributes.inputFeatures
          & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
          mInputChannel = new InputChannel(); // 关键核心点:创建 InputChannel 对象
      }
      // 通过 Binder 调用,进入 system_server 进程的 Session
      // 注意把 mInputChannel 作为参数传递给了 WMS
       res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
             getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
             mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
             mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
             mTempInsets);
      ......
      if (view instanceof RootViewSurfaceTaker) {
            mInputQueueCallback =
                 ((RootViewSurfaceTaker)view).willYouTakeTheInputQueue();
      }
      if (mInputChannel != null) {
          if (mInputQueueCallback != null) {
              mInputQueue = new InputQueue();
              mInputQueueCallback.onInputQueueCreated(mInputQueue);
          }
          // 创建 WindowInputEventReceiver 对象
          // 注意也是把 mInputChannel 作为参数传递给了 WindowInputEventReceiver
          mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                  Looper.myLooper());
      }
    }
}

该方法主要功能:

  • 创建 APP 端 Java 层的 InputChannel 对象 mInputChannel
  • 把这个 mInputChannel 通过 Binder 跨进程传递给 WMS,在 WMS 中通过 openInputChannelPair 创建 socket pair,将其中的 socket 客户端赋值给 mInputChannel
  • 创建 WindowInputEventReceiver 对象,并把 mInputChannel 传入其中,用来接收输入事件

需要注意的是:这里仅仅是 new 了一个 InputChanel,并未对其成员变量填充任何数据,也就是说这只是个空的 InputChanel,它的具体赋值是在 WMS 中完成的。

跨进程调用,进入 binder thread 执行如下方法:

2.4 Session.addToDisplay

public int addToDisplay(IWindow window, int seq, ......) {
   return mService.addWindow(this, window, seq, ......);
}

2.5 WMS.addWindow

public int addWindow(Session session, IWindow client, int seq, ......) {
    ......
    WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
    // 创建 WindowState,表示一个窗口
    final WindowState win = new WindowState(this, session, client, token,
          parentWindow, appOp[0], seq, attrs, viewVisibility,
          session.mUid, session.mCanAddInternalSystemWindow);
    ......
    // 这里的 outInputChannel 就是 APP 端传递过来的 InputChannel
    final boolean openInputChannels = (outInputChannel != null
                    && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
    if (openInputChannels) {
         win.openInputChannel(outInputChannel); // 创建 InputChannel
    }
    ......
    boolean focusChanged = false;
    if (win.canReceiveKeys()) {
        // 新添加 window 能接收按下操作,则更新聚焦窗口。
        focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
                false /*updateInputWindows*/);
    }
    ......    
    if (focusChanged) {
        displayContent.getInputMonitor().setInputFocusLw(
        displayContent.mCurrentFocus, false /*updateInputWindows*/);
    }
    // 设置当前聚焦窗口
    displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
}

2.5.1 WindowState.openInputChannel

WindowState.java

void openInputChannel(InputChannel outInputChannel) {
        if (mInputChannel != null) {
  throw new IllegalStateException("Window already has an input channel.");
        }
        String name = getName(); // 窗口名称
        // 创建一对 InputChannel
        InputChannel[] inputChannels =
            InputChannel.openInputChannelPair(name);
        mInputChannel = inputChannels[0]; // socket 服务端
        mClientChannel = inputChannels[1]; // socket 客户端
        // token 唯一标识了接收 input 事件的窗口
        mInputWindowHandle.token = mClient.asBinder();
        if (outInputChannel != null) {
        // socket 客户端传递给 app 进程的 outInputChannel
            mClientChannel.transferTo(outInputChannel);
            mClientChannel.dispose();
            mClientChannel = null;
        } else {
            ......
        }
        // 利用 socket 服务端作为参数
        mWmService.mInputManager.registerInputChannel(mInputChannel,
            mClient.asBinder());
}

这个方法是重点,主要构造一对 InputChannel,分为 “server” 和 “client”,“server” 最后会被注册到 InputDispatcher 中,而 “client” 会返回给 APP 进程的 ViewRootImpl。

inputChannels 数组:

  • inputChannels[0] 对应 InputChannel 服务端
  • inputChannels[1] 对应 InputChannel 客户端

其中:

  • 服务端 inputChannels[0] 保存到 WindowState 的 mInputChannel 中
  • 客户端 inputChannels[1] 传递给 outInputChannel,最终传递给 ViewRootImpl 的 mInputChannel

2.5.2 WindowState初始化

WindowState(WindowManagerService service, Session s, ......) {
    ......
    super(service);
    mSession = s;
    mClient = c;
    mAppOp = appOp;
    mToken = token;
    mAppToken = mToken.asAppWindowToken();
    ......
    // 创建 InputWindowHandle 对象
    mInputWindowHandle = new InputWindowHandle(
            mAppToken != null ? mAppToken.mInputApplicationHandle : null, c,
            getDisplayId());
}

2.5.3 updateInputWindowsLw

InputMonitor.java

void updateInputWindowsLw(boolean force) {
    if (!force && !mUpdateInputWindowsNeeded) {
       return;
    }
    scheduleUpdateInputWindows();
}


private void scheduleUpdateInputWindows() {
     if (mDisplayRemoved) {
        return;
     }
     if (!mUpdateInputWindowsPending) {
        mUpdateInputWindowsPending = true;
        mHandler.post(mUpdateInputWindows);
     }
}


private final Runnable mUpdateInputWindows = new Runnable() {
    @Override
    public void run() {
        synchronized (mService.mGlobalLock) {
            mUpdateInputWindowsPending = false;
            mUpdateInputWindowsNeeded = false;
            if (mDisplayRemoved) {
                return;
            }
            final boolean inDrag =
                 mService.mDragDropController.dragDropActiveLocked();
            final boolean inPositioning =
                 mService.mTaskPositioningController.isPositioningLocked();
            if (inPositioning) {
                if (DEBUG_TASK_POSITIONING) {
                   Log.d(TAG_WM, "Inserting window handle for repositioning");
                }
                mService.mTaskPositioningController.showInputSurface(
                    mInputTransaction, mDisplayId);
            } else {
                 mService.mTaskPositioningController.hideInputSurface(
                     mInputTransaction, mDisplayId);
            }
            // Add all windows on the default display.
            mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
        }
    }
};

最后通过 SurfaceComposerClient 更新到 SurfaceFlinger,然后最终更新到 InputDispatcher

2.6 InputChannel.openInputChannelPair

InputChannel.java

public static InputChannel[] openInputChannelPair(String name) {
        if (name == null) {
            throw new IllegalArgumentException("name must not be null");
        }
        if (DEBUG) {
            Slog.d(TAG, "Opening input channel pair '" + name + "'");
        }
        return nativeOpenInputChannelPair(name);
}

2.6.1 nativeOpenInputChannelPair

android_view_InputChannel.cpp

tatic 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;
    // 创建一对 native 层 InputChannel
    status_t result = InputChannel::openInputChannelPair(
            name, serverChannel, clientChannel);

    ......
    // 构造一个 java 层 InputChannel 类型数组
    jobjectArray channelPair = env->NewObjectArray(2,
            gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }
    // 将 native 层 InputChannel 转换为 java 层 InputChannel
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(serverChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }
    // 将 native 层 InputChannel 转换为 java 层 InputChannel
    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            std::make_unique<NativeInputChannel>(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }
    // 将转换的 java 层 InputChannel 存到前面构造的数组中
    env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
    env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
    return channelPair;

这个函数很简单,主要是通过 openInputChannelPair 构造了一对 native 层的 InputChannel,然后再根据其创建 java 层的 InputChannel,最后返回给 java 层。

2.6.2 InputChannel::openInputChannelPair

status_t InputChannel::openInputChannelPair(const std::string& name,
        sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
    int sockets[2];
    // 关键核心:真正创建 socketpair 的地方
    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; // 32KB
    // 设置 socket 发送和接收缓冲区大小为 bufferSize 
    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)";
    // server 端 InputChannel 保存了 server 端 socket 的 fd
    outServerChannel = new InputChannel(serverChannelName, sockets[0]);

    std::string clientChannelName = name;
    clientChannelName += " (client)";
    // client 端 InputChannel 保存了 client 端 socket 的 fd
    outClientChannel = new InputChannel(clientChannelName, sockets[1]);
    return OK;
}

该方法主要功能:

  • 创建 socket pair (非阻塞式的 socket )
  • 设置两个 socket 的接收和发送的 buffer上限为 32KB
  • 创建 client 和 server 的 Native 层 InputChannel 对象

创建 InputChannel 对象位于文件 InputTransport.cpp,如下:

InputChannel::InputChannel(const std::string& name, int fd) :
        mName(name) {
    setFd(fd);
}

void InputChannel::setFd(int fd) {
    if (mFd > 0) {
        ::close(mFd);
    }
    mFd = fd;
    if (mFd > 0) {
        // 将 socket 设置成非阻塞方式
        int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
    }
}

另外,创建 NativeInputChannel 对象位于文件 android_view_InputChannel.cpp,如下:

NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
    mInputChannel(inputChannel), mDisposeCallback(NULL) {
}

2.6.3 android_view_InputChannel_createInputChannel

static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
        std::unique_ptr<NativeInputChannel> nativeInputChannel) {
    // 创建 Java 的 InputChannel
    jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);
    if (inputChannelObj) {
        // 将 nativeInputChannel 保存到 Java 层的 InputChannel 的成员变量 mPtr
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj,
                 nativeInputChannel.release());
    }
    return inputChannelObj;
}



static void android_view_InputChannel_setNativeInputChannel(
    JNIEnv* env, jobject inputChannelObj,
    NativeInputChannel* nativeInputChannel) {
    env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
             reinterpret_cast<jlong>(nativeInputChannel));
}

此处:

  • gInputChannelClassInfo.clazz 是指 Java 层的 InputChannel 类
  • gInputChannelClassInfo.ctor 是指 Java 层的 InputChannel 构造方法
  • gInputChannelClassInfo.mPtr 是指 Java 层的 InputChannel 的成员变量 mPtr

2.7 InputChannel.transferTo

public void transferTo(InputChannel outParameter) {
        if (outParameter == null) {
            throw new IllegalArgumentException("outParameter must not be null");
        }
        nativeTransferTo(outParameter);
}

2.7.1 nativeTransferTo

static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
        jobject otherObj) {
    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Other object already has a native input channel.");
        return; // 当 Java 层的 InputChannel.mPtr 不为空,则返回
    }
    // 将当前 inputChannels[1] 的 mPtr 赋值给 nativeInputChannel
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, obj);
    // 将该 nativeInputChannel 保存到 outInputChannel 的参数
    android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}


static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobject inputChannelObj) {
    jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast<NativeInputChannel*>(longPtr);
}

inputChannels[1].transferTo(outInputChannel) 主要功能:

  • 如果 outInputChannel.mPtr 不为空(初始状态是空的),则直接返回;否则进入 step2
  • 将 inputChannels[1].mPtr 的值赋给 outInputChannel.mPtr
  • 清空 inputChannels[1].mPtr 值

也就是将 socket 客户端 inputChannels[1] 传递给 outInputChannel

2.8 IMS.registerInputChannel

public void registerInputChannel(InputChannel inputChannel, IBinder token) {
        if (inputChannel == null) {
            throw new IllegalArgumentException("inputChannel must not be null.");
        }
        if (token == null) {
            token = new Binder();
        }
        // 这个 inputChannel 保存了这个窗口的 token (IWindow,对应 app 端 W)
        inputChannel.setToken(token); 
        nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
}

inputChannel 是指 inputChannels[0],即 socket 服务端

2.8.1 nativeRegisterInputChannel

com_android_server_input_InputManagerService.cpp

static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobject inputChannelObj, jint displayId) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == nullptr) {
        throwInputChannelNotInitialized(env);
        return;
    }

    status_t status = im->registerInputChannel(env, inputChannel, displayId);

    if (status) {
        std::string message;
        message += 
        StringPrintf("Failed to register input channel.  status=%d", status);
        jniThrowRuntimeException(env, message.c_str());
        return;
    }

    android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
            handleInputChannelDisposed, im);
}

2.8.2 NativeInputManager::registerInputChannel

com_android_server_input_InputManagerService.cpp

status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
        const sp<InputChannel>& inputChannel, int32_t displayId) {
    ATRACE_CALL();
    return mInputManager->getDispatcher()->registerInputChannel(
            inputChannel, displayId);
}

2.8.3 InputDispatcher::registerInputChannel

InputDispatcher.cpp

核心重点函数

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        int32_t displayId) {
    { // acquire lock
        std::scoped_lock _l(mLock);

        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().c_str());
            return BAD_VALUE;
        }
        // 核心关键点:创建 Connection,并把 inputChannel 作为参数
        sp<Connection> connection = new Connection(inputChannel, false /*monitor*/);

        int fd = inputChannel->getFd();
        mConnectionsByFd.add(fd, connection);// 以 socket 文件名为索引添加 connection
        // 以 token 为索引添加 inputChannel
        mInputChannelsByToken[inputChannel->getToken()] = inputChannel;
        // 将该 socket fd 添加到 Looper 监听
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    // Wake the looper because some connections have changed.
    mLooper->wake();//connection改变, 则唤醒looper
    return OK;
}
  • 新创建 connection,并以 socket fd 为 key 保存到 mConnectionsByFd 成员变量中
  • 以 inputChannel 的 token 为 key,保存 inputChannel 到 mInputChannelsByToken 中
  • InputDispatcher 线程的 Looper 添加对 socket 服务端 fd 的监听功能,这样当该 socket 有消息时便会唤醒该线程

到此 InputChannel ”server端“ 的注册过程已经完成。

2.8.4 初始化Connection

InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel, bool monitor) :
        status(STATUS_NORMAL), inputChannel(inputChannel),
        monitor(monitor),
        inputPublisher(inputChannel), inputPublisherBlocked(false) {
}

其中 InputPublisher 初始化位于文件 InputTransport.cpp

InputPublisher:: InputPublisher(const sp<InputChannel>& channel) :
        mChannel(channel) {
}

此处 inputChannel 是指前面 openInputChannelPair 创建的 socket 服务端,将其同时保存到 Connection.inputChannel 和 InputPublisher.mChannel 中。

2.8.5 Looper.addFd

system/core/libutils/Looper.cpp

int Looper::addFd(int fd, int ident, int events,
        Looper_callbackFunc callback, void* data) {
    // 此处的 callback 为 handleReceiveCallback 
    return addFd(fd, ident, events, callback ?
           new SimpleLooperCallback(callback) : NULL, data);
}

int Looper::addFd(int fd, int ident, int events,
        const sp<LooperCallback>& callback, void* data) {
    {
        AutoMutex _l(mLock);
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.events = events;
        request.seq = mNextRequestSeq++;
        request.callback = callback; // 是指 SimpleLooperCallback
        request.data = data;
        if (mNextRequestSeq == -1) mNextRequestSeq = 0;
        
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            // 通过 epoll 监听 fd
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            ......
            mRequests.add(fd, request); // 该 fd 的 request 加入到 mRequests 队列
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            ......
            mRequests.replaceValueAt(requestIndex, request);
        }
    } 
    return 1;
}

此处 Loop 便是 InputDispatcher 线程的 Looper,将 socket 服务端的 fd 采用 epoll 机制注册监听。

小节

ViewRootImpl 的 setView() 过程:

1.创建 socket pair,作为 InputChannel

  • socket 服务端保存到 system_server 中的 WindowState 的 mInputChannel
  • socket 客户端通过 binder 传回到远程进程的 UI 主线程 ViewRootImpl 的 mInputChannel

2.IMS.registerInputChannel() 注册 InputChannel,监听 socket 服务端

  • Loop 便是 InputDispatcher 线程的 Looper
  • 回调方法 handleReceiveCallback

三 WindowInputEventReceiver

ViewRootImpl.java

3.1 WindowInputEventReceiver初始化

final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel,
            Looper looper) {
            super(inputChannel, looper);
        }
        @Override
        public void onInputEvent(InputEvent event) {
            List<InputEvent> processedEvents;
            try {
                processedEvents =
                mInputCompatProcessor.processInputEventForCompatibility(event);
            } finally {
                ......
            }
            if (processedEvents != null) {
                if (processedEvents.isEmpty()) {
                    // InputEvent consumed by mInputCompatProcessor
                    finishInputEvent(event, true);
                } else {
                    for (int i = 0; i < processedEvents.size(); i++) {
                        enqueueInputEvent(
                        processedEvents.get(i), this,
                        QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
                    }
                }
            } else {
                enqueueInputEvent(event, this, 0, true);
            }
        }

        @Override
        public void onBatchedInputEventPending() {
            if (mUnbufferedInputDispatch) {
                super.onBatchedInputEventPending();
            } else {
                scheduleConsumeBatchedInput();
            }
        }

        @Override
        public void dispose() {
            unscheduleConsumeBatchedInput();
            super.dispose();
        }
}

3.2 InputEventReceiver

public abstract class InputEventReceiver {
    ......
    private long mReceiverPtr;
    
    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(); // UI 线程消息队列
        mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
                inputChannel, mMessageQueue);

        mCloseGuard.open("dispose");
    }
    ......
}

3.3 nativeInit

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);
    ......
    // 获取 UI 主线程的消息队列
    sp<MessageQueue> messageQueue = 
    android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    ......
    // 创建 NativeInputEventReceiver 对象
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    status_t status = receiver->initialize();
    if (status) {
        ......
    }
    // retain a reference for the object
    receiver->incStrong(gInputEventReceiverClassInfo.clazz); 
    return reinterpret_cast<jlong>(receiver.get());
}

3.4 NativeInputEventReceiver

class NativeInputEventReceiver : public LooperCallback {
    InputConsumer mInputConsumer;
    sp<MessageQueue> mMessageQueue;
    int mFdEvents;
    bool mBatchedInputEventPending;
    ......
};
    
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) {
       ......           
}

3.4.1 InputConsumer

InputTransport.cpp

InputConsumer::InputConsumer(const sp<InputChannel>& channel) :
        mResampleTouch(isTouchResamplingEnabled()),
        mChannel(channel), mMsgDeferred(false) {
}

3.5 NativeInputEventReceiver::initialize

android_view_InputEventReceiver.cpp

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

3.6 NativeInputEventReceiver::setFdEvents

android_view_InputEventReceiver.cpp

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);
        }
    }
}

3.6.1 Looper.addFd

system/core/libutils/Looper.cpp

int Looper::addFd(int fd, int ident, int events,
        const sp<LooperCallback>& callback, void* data) {
    {
        AutoMutex _l(mLock);
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.events = events;
        request.seq = mNextRequestSeq++;
        request.callback = callback; // 是指 NativeInputEventReceiver
        request.data = data;
        if (mNextRequestSeq == -1) mNextRequestSeq = 0;
        
        struct epoll_event eventItem;
        request.initEventItem(&eventItem);

        ssize_t requestIndex = mRequests.indexOfKey(fd);
        if (requestIndex < 0) {
            // 通过 epoll 监听 fd
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            ......
            mRequests.add(fd, request); // 该fd 的 request 加入到 mRequests 队列
        } else {
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            ......
            mRequests.replaceValueAt(requestIndex, request);
        }
    } 
    return 1;
}

此处的 Looper 便是 UI 主线程的 Looper,将 socket 客户端的 fd 添加到 UI 线程的 Looper 来监听,回调方法为 NativeInputEventReceiver。

四 总结

4.1 小结

  • 首先当一个 APP 启动时,会将自己的 Window 添加到 WMS,并传递一个空 InputChannel 过去
  • WMS 端,通过 openInputChannel 方法会创建一对 InputChannel,是在 native 层完成的,这对 InputChannel 被分为 “client” 端和 “server” 端,其内部又会创建一对 socket,和这对 InputChannel 一一对应
  • “server” 端 InputChannel 会被注册到 InputDispatcher 中,注册的原理就是将 InputChannel 内部的 socket 添加到其 Looper 进行监听,注册过程中还会创建一个 Connection 对象,Connection 用来描述 InputDispatcher 与此次注册 InputChannel 的窗口的连接
  • “client” 端 InputChannel 会被设置到 APP 进程中,接着通过 InputEventReceiver 注册到 APP UI 线程,同样是将 InputChannel 内部的 socket 添加到 UI 线程的 Looper 进行监听
  • 对于 InputDispatcher 线程,在接收到 “client” 端 socket 的消息时会回调其 handleReceiveCallback 函数,对于 APP UI 线程,在接收到 “server” 端 socket 的消息时会回调 InputEventReceiver 对应的 native 层对象 NativeInputEventReceiver 的 handleEvent 函数

4.2 流程图

在这里插入图片描述
有了这些架构的实现,InputDispatcher 和 UI 主线程便可以进行跨进程通信与交互了。如有问题欢迎留言,谢谢!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值