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 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值