一 概述
前面介绍了 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