framework 学习笔记23. input输入事件番外6(事件分发 APP获取事件)

上一节中讲到 dispatchMotionLocked() 向目标窗口分发事件,接下来继续学习目标窗口是如何获取和绑定的;窗口的绑定是在 Activity 的启动流程中(具体可以参考 笔记14 Activity 启动流程),这里先简单介绍一下 Activity 的创建。

1. Activity 的创建

其中跨进程通信完成后,使用 ActivityThread 的内部类 ApplicationThread 中 scheduleLaunchActivity() 方法进行处理了:其实在这个方法中也很简单,就是使用主线程的 handler 发送了一条 H.LAUNCH_ACTIVITY 的消息,然后 handle r处理消息时调用了 handleLaunchActivity(r, null) 方法,具体有以下两步:

(1)创建 activity 第 1 步 activity.attach():
handleLaunchActivity() -> performLaunchActivity() -> activity.attach(),这个 activity.attach() 方法,是对 activity进行绑定,完成这一步activity才成为四大组件之一,未完成时都只能算一个对象。
(2)创建 activity 第 2 步 handleResumeActivity():
handleLaunchActivity() -> handleResumeActivity(),执行 onResume() 方法并渲染;

1.1 activity.attach():

// activity 的 attach() 方法:
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, IVoiceInteractor voiceInteractor) {

    attachBaseContext(context);
    mFragments.attachActivity(this, mContainer, null);
    // 创建 Window
    // 这里的 mWindow 时 PhoneWindow,后续版本的 mWindow 初始化如下,更加直观:
    // mWindow = new PhoneWindow(this, window, activityConfigCallback);
    mWindow = PolicyManager.makeNewWindow(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    // ...
    // 在 window 中创建时 mWindowManager 其实是 WindowManagerImpl,代码不复杂,可以跟进去看看
    mWindow.setWindowManager(  // 设置 WindowManager
            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
            mToken, mComponent.flattenToString(),
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
    if (mParent != null) {
        mWindow.setContainer(mParent.getWindow());
    }
    mWindowManager = mWindow.getWindowManager();  // 这里就可以获取 WindowManager 了
    mCurrentConfig = config;
}

在这里记住三个知识点(后续讲setContentView时再详细分析):
a. Window 类是一个抽象类,它的唯一实现类是 PhoneWindow;
b. PhoneWindow 有一个内部类 DecorView,DecorView 是 Activity 的根 View;
c. DecorView 继承自 FramLayout;

关于创建Window对象:
PolicyManager 为策略类,其实现类 Policy 的makeNewWindow内部创建了window对象;

// mWindow = PolicyManager.makeNewWindow(this):
public final class PolicyManager {
    private static final String POLICY_IMPL_CLASS_NAME =
        "com.android.internal.policy.impl.Policy";

    private static final IPolicy sPolicy;

    static {  // 通过反射创建 sPolicy
        try {
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME); 
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {
            // ...
        }
    }

    private PolicyManager() {}

    // 创建PhoneWindow
    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }

    public static LayoutInflater makeNewLayoutInflater(Context context) {
        return sPolicy.makeNewLayoutInflater(context);
    }

    public static WindowManagerPolicy makeNewWindowManager() {
        return sPolicy.makeNewWindowManager();
    }

    public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
        return sPolicy.makeNewFallbackEventHandler(context);
    }
}

// Policy.java 类中的 makeNewWindow() 方法:
public window makeNewWindow(Context context){
        return new PhoneWindow(context);
}

1.2 handleResumeActivity():

    // 第二步:
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        
        // 主要作用是调用 performResumeActivity() 到 activity 的 onResume 状态,然后获取
        // DecorView,创建一个关联的 ViewRootImpl 对象,用来配合 WindowManagerService 服
        // 务来管理该 Activity 组件的窗口状态,最后 addView
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;
            // ...

            //activity创建成功,window此时为空,进入此分支;
            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 = true;
                    // 一层层的看最终调用的是:WindowManagerGlobal.java -> addView()
                    wm.addView(decor, l);  // 这里就是测量,摆放,绘制;  也是下面要讲的内容;
                }

            } else if (!willBeVisible) {
                r.hideForNow = true;
            }
            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r);

            // ...
        } 
    }

2. 从 addView() 到 服务端(WMS) InputChannel (也就是 fd ) 的注册

整个过程如下所示:服务端的注册即 WMS 中 InputChannel 注册到 InputDispatcher;


Input输入事件 - App与输入系统的联系.png

2.1 从 addView 开始:

// WindowManagerImpl.addView():
    // 单例获取 WindowManagerGlobal
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }


// 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;

            //...
     
            // 实例化 ViewRootImpl
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            // 添加 view 到全局集合中
            // 如果想 hook 全部的 view 时,可以通过反射获取 WindowManagerGlobal -> mViews;
            mViews.add(view);  
            mRoots.add(root);
            mParams.add(wparams);
        }
       
        // do this last because it fires off messages to start doing things
        try {  // 将 view 添加到 ViewRootImpl 中去
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
           // ...
        }
    }

ViewRootImpl.setView():

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
        // 这里先将 mView 保存了 DecorView 的实例,
        // 然后调用 requestLayout() 方法,以完成应用程序用户界面的初次布局。
        if (mView == null) {
            mView = view;
            // mWindowAttributes 保存了窗口所对应的 LayoutParams
           mWindowAttributes.copyFrom(attrs);
            /**
            * 在添加窗口之前,先通过requestLayout方法在主线程上安排一次“遍历”。
            * 所谓“遍历”是指 ViewRootImpl 中的核心方法 performTraversal()。
            * 这个方法实现对控件树进行测量、布局、向 WMS 申请修改窗口属性以及重绘的所有工作。
            */
           requestLayout();  
           /*初始化 mInputChannel。InputChannel 是窗口接受来自 InputDispatcher 的输入事件的管
             道。注意,仅当窗口的属性 inputFeatures 不含有 INPUT_FEATURE_NO_INPUT_CHANNEL 时
             才会创建 InputChannel,否则 mInputChannel 为空,从而导致此窗口无法接受任何输入事件 */
           if ((mWindowAttributes.inputFeatures
                   & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
               mInputChannel = new InputChannel();  // 见 注释1
           }
           try {
             // ...
             /* 将窗口添加到 WMS 中。完成这个操作之后,mWindow 已经被添加到指定的Display中去
               而且 mInputChannel(如果不为空)已经准备好接受事件了。只是由于这个窗口没有进行
               过 relayout() ,因此它还没有有效的 Surface 可以进行绘制 */
             res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                       getHostVisibility(), mDisplay.getDisplayId(),
                       mAttachInfo.mContentInsets, mInputChannel);  // 重点代码
              // 这里的 mWindowSession 是向WMS跨进程请求获取的 
           } catch (RemoteException e) {  
              // ... 
           } finally { 
              // ... 
           }

        }
    }

(注释1)InputChannel的构造函数:此时 mInputChannel = new InputChannel() 这里还是一个 Java 的对象;

public final class InputChannel implements Parcelable {
    private static final String TAG = "InputChannel";
    
    @SuppressWarnings("unused")
    private long mPtr; // used by native code
    private static native InputChannel[] nativeOpenInputChannelPair(String name);
    
    private native void nativeDispose(boolean finalized);
    private native void nativeTransferTo(InputChannel other);
    private native void nativeReadFromParcel(Parcel parcel);
    private native void nativeWriteToParcel(Parcel parcel);
    private native void nativeDup(InputChannel target);
    
    private native String nativeGetName();

    // 构造函数中没有任何操作,此时 mInputChannel = new InputChannel() 只是一个普通的java对象;
    // 那么要想具有 c++ 的属性,唯一的方法就是持有 c++ 对象的指针,也就是将 mPtr 赋值;
    public InputChannel() {
    }
    // ...
  }

2.2 mWindowSession.addToDisplay():重点的开始
(1)首先,mWindowSession 是如何获取到的:ViewRootImpl 的构造函数中进行初始化的;

// mWindowSession 的初始化:ViewRootImpl 的构造函数中进行初始化的;
public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();  // 通过 WMS 获取
}


// WindowManagerGlobal 类中的 getWindowSession() 方法
public static IWindowSession getWindowSession() {
    synchronized (WindowManagerGlobal.class) {
        if (sWindowSession == null) {
            try {
                InputMethodManager imm = InputMethodManager.getInstance();
                IWindowManager windowManager = getWindowManagerService();
                // 通过 WMS 获取 mWindowSession
                sWindowSession = windowManager.openSession(
                        new IWindowSessionCallback.Stub() {
                            @Override
                            public void onAnimatorScaleChanged(float scale) {
                                ValueAnimator.setDurationScale(scale);
                            }
                        },
                        imm.getClient(), imm.getInputContext());
                ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to open window session", e);
            }
        }
        return sWindowSession;
    }
}


// WMS 中 openSession():
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
        IInputContext inputContext) {
    if (client == null) throw new IllegalArgumentException("null client");
    if (inputContext == null) throw new IllegalArgumentException("null inputContext");
    Session session = new Session(this, callback, client, inputContext);  // 参数 this 就是 WMS;
    return session;
}

IWindowSession:一个aidl接口,它的真的实现类是Session,是一个 Binder 对象,也是 ViewRootImpl 和 WMS 进行通信的代理。在 ViewRootImpl 的 setView() 中最终也是通过它和 WMS 通信完成了 Window 的添加。这个 Session 是应用唯一的,它的创建是在 WindowManagerGloable 中通过 getWindowSession() 获取的。

(2)mWindowSession.addToDisplay():就是调用了 Session 的 addToDisplay() 方法;

// Session 的 addToDisplay() 方法:又是跨进程通讯
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets,
            InputChannel outInputChannel) {

        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outInputChannel);   // 调用了 WMS 中的 addWindow() 方法
    }

该方法的参数 outInputChannel 是 ViewRootImpl.setView() 中 mInputChannel = new InputChannel(),不过此时 InputChannel 中的 c++ 对象指针还未赋值,mService 就是 new Session 时传入的 WMS。

(3)WMS 的addWindow() 方法:App 端 InputChannel 赋值与建立 socketpair 双向通信;

public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, InputChannel outInputChannel) {
        int[] appOp = new int[1];
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }

        boolean reportNewConfig = false;
        WindowState attachedWindow = null;
        WindowState win = null;  // window 对象的信息
        long origId;
        final int type = attrs.type;

        synchronized(mWindowMap) {
            // ...
            win = new WindowState(this, session, client, token, 
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
            // ...
            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                String name = win.makeInputChannelName();
                // 关键代码,打开一对 InputChannel,客户端和服务端
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); 
                // WMS 设置 Channel 为 inputChannels[0] 
                win.setInputChannel(inputChannels[0]); 
                // 客户端设置 Channel 为 inputChannels[1],此时给上面 Java层的 mInputChannel 中 mPtr 赋值
                // 经过赋值后,Java 层 mInputChannel 才具有 c++ 的对象
                inputChannels[1].transferTo(outInputChannel);  

                // 将服务端的 socket 注册到 InputDispatcher 中 
                // 这里的 win.mInputWindowHandle 是在 win 初始化的时候 new 出来的;
                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }

            // ...
            // 每次添加都会更新
            mInputMonitor.updateInputWindowsLw(false /*force*/);
            // ...
        }
        // ...
}

在 WMS 的 addWindow() 方法中有很多的关键点,这里列举一些下面内容中需要关注的关键点:

  • (a)win = new WindowState():创建出 WindowState 来描述 App 端的信息;
  • (b)InputChannel.openInputChannelPair():使用 socketpair() 打开一对 InputChannel;
  • (c)win.setInputChannel(inputChannels[0]):设置服务端 WMS 的 InputChannel 为 inputChannels[0];
  • (d)inputChannels[1].transferTo(outInputChannel):客户端设置 Channel 为 inputChannels[1],给上面 Java层的 mInputChannel 中 mPtr 赋值,经过赋值后,Java 层 mInputChannel 才具有了 c++ 的对象;
  • (e)mInputManager.registerInputChannel():将服务端的 InputChannel 注册到 InputDispatcher 中 ;

2.3 双向通信 socketpair 的建立:承接上面的关键点进行分析;
(1)InputChannel.openInputChannelPair():在 InputChannel.java 中很简单,只是调用了一个 native 方法 nativeOpenInputChannelPair(name),在 frameworks/base/core/jni/android_view_InputChannel.cpp 中:

// android_view_InputChannel.cpp 中:
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;
    // 第一步(详见下方代码):创建一对 socket 通信;   
    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;
    }

    // 第二步:创建 java 数组对象;用于保存这对 InputChannel
    jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
    if (env->ExceptionCheck()) {
        return NULL;
    }

    // 第三步(详见下方代码):创建 NativeInputChannel,将 InputChannel 指针保存于 mInputChannel 中;
    // 第四步  (详见下方代码):设置 Java InputChannel 的 mPtr 成员变量;
    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(serverChannel));  
    if (env->ExceptionCheck()) {
        return NULL;
    }

    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
            new NativeInputChannel(clientChannel));
    if (env->ExceptionCheck()) {
        return NULL;
    }

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


// 第一步:创建一对 InputChannel;(InputChannel.cpp中)
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;
    // ...    
    // 封装成 InputChannel
    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::InputChannel(const String8& name, int fd) :
        mName(name), mFd(fd) {
    // 代码很简单,有用的只有一行(将 fd 设置成为非阻塞方式)
    int result = fcntl(mFd, F_SETFL, O_NONBLOCK);
}

// 第二步:创建 java 数组对象;用于保存这对 InputChannel(通过调用 jni 的方法,省略介绍)

// 第三步:创建 NativeInputChannel,将传入的 InputChannel 指针保存于 mInputChannel 成员变量中;
NativeInputChannel::NativeInputChannel(const sp<InputChannel>& inputChannel) :
    mInputChannel(inputChannel), mDisposeCallback(NULL) {
}

// 第四步:
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
        NativeInputChannel* nativeInputChannel) {
    jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
            gInputChannelClassInfo.ctor);  // 调用 jni 方法 NewObject 创建一个 Java InputChannel 对象
    if (inputChannelObj) {
        // 调用 android_view_InputChannel_setNativeInputChannel 将 nativeInputChannel 对象指针强
        // 转为 jlong,然后将其设置到 Java InputChannel 类的 mPtr 成员变量上;
        android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
    }
    return inputChannelObj;
}

经过 InputChannel.cpp 中 openInputChannelPair() 中调用 socketpair(),并封装成一对 InputChannel,然后转换成 Java 对象返回给 WMS 供其使用(这里注意一下,客户端的 InputChannel 是 WMS 获取到之后,经过 Binder 通信发送给 app 的,这一套 socketpair + Binder 的通信方式,实现了任意进程间的双向通信);另外,socketpair() 在前面的【输入事件番外篇1 Linux知识点】中有讲过。

(2)设置服务端(WMS)和客户端(App)端的 InputChannel:

// 设置 WMS 的 InputChannel:win.setInputChannel(inputChannels[0])
// WindowState.java 中
void setInputChannel(InputChannel inputChannel) {
    if (mInputChannel != null) {
        throw new IllegalStateException("Window already has an input channel.");
    }
    // 代码很简单,只需要把 WindowState 中的两个成员变量的值置为传入的参数 inputChannels[0];
    mInputChannel = inputChannel;
    mInputWindowHandle.inputChannel = inputChannel;
}



// 设置 App 的 InputChannel:inputChannels[1].transferTo(outInputChannel)
// InputChannel.java 中
public void transferTo(InputChannel outParameter) {
    if (outParameter == null) {
        throw new IllegalArgumentException("outParameter must not be null");
    }
    // 执行了native 方法,调用了 android_view_InputChannel.cpp 中的方法
    // 将 InputChannel 内部状态的所有权转移到另一个实例,并使该实例无效。
    nativeTransferTo(outParameter);
}

// android_view_InputChannel.cpp 中
static void android_view_InputChannel_nativeTransferTo(JNIEnv* env, jobject obj,
        jobject otherObj) {

    /* otherObj 即对应的入参,调用 android_view_InputChannel_getNativeInputChannel 方法可将 Java 
        InputChannel 对象的 mPtr 成员变量强转为 NativeInputChannel 对象,由于传递过来的 Java 层的
        InputChannel 对象中 mPtr 还没有赋值,是一个个“空壳”,所以强转以后一定为空,否则抛出异常 */
    if (android_view_InputChannel_getNativeInputChannel(env, otherObj) != NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
                "Other object already has a native input channel.");
        return;
    }

    // 这次调用是将 obj 转化为 NativeInputChannel 对象。
    NativeInputChannel* nativeInputChannel =
            android_view_InputChannel_getNativeInputChannel(env, obj);
    // 将obj 转化的 NativeInputChannel 对象(实际是指针)设置到 otherObj mPtr 成员变量上。*/
    android_view_InputChannel_setNativeInputChannel(env, otherObj, nativeInputChannel);
    // 最后将 obj mPtr 成员变量设置为 0,即表示 NativeInputChannel 对象为 NULL。
    android_view_InputChannel_setNativeInputChannel(env, obj, NULL);
}

// 这两个被调用的方法,主要调用 jni 方法 GetLongField 和 SetLongField,实现 Native 层操作 java 对象成员变量。
static NativeInputChannel* android_view_InputChannel_getNativeInputChannel(JNIEnv* env,
        jobject inputChannelObj) {
    jlong longPtr = env->GetLongField(inputChannelObj, gInputChannelClassInfo.mPtr);
    return reinterpret_cast<NativeInputChannel*>(longPtr);
}
static void android_view_InputChannel_setNativeInputChannel(JNIEnv* env, jobject inputChannelObj,
        NativeInputChannel* nativeInputChannel) {
    env->SetLongField(inputChannelObj, gInputChannelClassInfo.mPtr,
             reinterpret_cast<jlong>(nativeInputChannel));
}

经过上面的赋值操作,WMS 端和 App 端各自持有了一对经过 socketpair 创建封装的 InputChannel,那么接下来就是看看如何与事件分发系统联系起来了;

(3)mInputManager.registerInputChannel():将 WMS 端的 InputChannel 注册到 InputDispatcher 中;这里的 mInputManager 是 IMS,在 WMS 的构造函数中作为参数传入;也就是说调用的是 IMS 的 registerInputChannel() 方法:

// InputManagerService.java 中:
public void registerInputChannel(InputChannel inputChannel,
        InputWindowHandle inputWindowHandle) {
    if (inputChannel == null) {
        throw new IllegalArgumentException("inputChannel must not be null.");
    }
    // native 方法
    nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false);
}
// frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
        jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {

    // 将 ptr 强转为 NativeInputManager 对象 (这里的 ptr 是 IMS 的 c++ 指针,不是 InputChannel 的)
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    // 将 inputChannelObj 转化为 NativeInputChannel 对象
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        throwInputChannelNotInitialized(env);
        return;
    }

    // 将 inputWindowHandleObj 转化为 NativeInputWindowHandle 对象
    sp<InputWindowHandle> inputWindowHandle =
            android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);

    // 最终调用 NativeInputManager 类 registerInputChannel() 方法完成实际注册工作
    status_t status = im->registerInputChannel(
            env, inputChannel, inputWindowHandle, monitor);
    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);
    }
}
// com_android_server_input_InputManagerService.cpp 中:
// 这里是一个过渡的方法,主要是获取到之前 IMS 启动时创建的 InputDispatcher,执行里面的方法;
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
        const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
    return mInputManager->getDispatcher()->registerInputChannel(  // 回到了 InputDispatcher 中了
            inputChannel, inputWindowHandle, monitor);
}
// frameworks/native/services/inputflinger/InputDispatcher.cpp 中:
// 在这个方法中,WMS 的 InputChannel 终于注册到 InputDispatcher 中了,也就是说,当有事件分发时,服
// 务端的 InputChannel 获取到事件分发然后通过数据通道发送到 App 端了;其主要的工作有以下四步:

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {

    { // acquire lock
        AutoMutex _l(mLock);
        // 第一步:检查 InputChannel 是否已经注册过
        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
            return BAD_VALUE;
        }

        // 第二步:创建 Connection 对象,Connection 表示客户端和服务端的一个输入数据通道
        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
        int fd = inputChannel->getFd();  // // 获取 socketpair 的 fd
        // 第三步:以 fd 为 key 将 Connection 添加到容器(mConnectionsByFd)中
        mConnectionsByFd.add(fd, connection);

        if (monitor) {
            mMonitoringChannels.push(inputChannel);
        }
        
        /* 第四步:将 fd 添加到 Looper 监听列表中。一旦对端的 Socket 写入数据,Looper 就会被唤醒,接着
           就会调用 handleReceiveCallback */
        mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
    } // release lock

    //  唤醒 Looper,因为一些连接(Connection)已经改变
    mLooper->wake();
    return OK;
}

经过这一步,WMS 的 InputChannel 就和 InputDispatcher 建立了关联。现在该回到 ViewRootImpl.setView() 方法中具体分析如何关联客户端 InputChannel 了。

3. App 端 InputChannel (也就是 fd ) 的注册

在上面 WindowManagerGlobal.addView() -> ViewRootImpl.setView() -> Session.addToDisplay -> WMS.addWindow() 最终完成了 WMS 端的 InputChannel 的注册;接下来分析客户端 InputDispatcher 的关联,也是从 ViewRootImpl.setView() 这个方法中开始的:

    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            // ... 省略部分代码

            // 在add to window manager之前执行第一次 requestLayout,以确保在 requestLayout 之前已经接收
            // 到系统的任何事件;
            requestLayout();
            if ((mWindowAttributes.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                mInputChannel = new InputChannel();
            }
            try {
                // ...
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mInputChannel);
            } catch (RemoteException e) {
                //...
            } finally {
                //...                   
            }

            // ... 省略部分代码

            if (mInputChannel != null) {
                if (mInputQueueCallback != null) {
                    mInputQueue = new InputQueue();
                    mInputQueueCallback.onInputQueueCreated(mInputQueue);
                }
                // 将 InputChannel 封装成 WindowInputEventReceiver,这里是重点
                mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                        Looper.myLooper());  // 注意一下,这里使用的是主线程的 Looper;
            }
            // ... 省略部分代码    
        }
    }

将 App 端的 InputChannel 封装成 WindowInputEventReceiver,这里也没有执行它的任何方法,那就看一下这个 WindowInputEventReceiver 的构造函数,看看有没有执行一些方法:

(1)封装 WindowInputEventReceiver:从类名上来看,这个类是 App 端窗口的事件接收器;

// WindowInputEventReceiver.java 中:ViewRootImpl 的内部类;
final class WindowInputEventReceiver extends InputEventReceiver {
    public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
        super(inputChannel, looper);  // 执行了父类中的方法,继续跟进
    }

    @Override
    public void onInputEvent(InputEvent event) {
        enqueueInputEvent(event, this, 0, true);
    }

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

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


// InputEventReceiver.java 中:
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();
    mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
            inputChannel, mMessageQueue);  // 这里就执行到 c++ 代码中了
    mCloseGuard.open("dispose");
    }

InputEventReceiver 构造器中首先检查入参 inputChannel 和 looper 是否为 null,接着从 looper 中获取 MessageQueue,最后调用 nativeInit() 进一步初始化;

(2)JNI 层初始化:nativeInit();

// frameworks/base/core/jni/android_view_InputEventReceiver.cpp

// 主要有以下四个点:
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
        jobject inputChannelObj, jobject messageQueueObj) {
    
    //(1)将 Java 层对象 InputChannel 对象转化为 NativeInputChannel 对象
    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
            inputChannelObj);
    if (inputChannel == NULL) {
        jniThrowRuntimeException(env, "InputChannel is not initialized.");
        return 0;
    }
    //(2)将 Java 层 MessageQueue 对象转化为 NativeMessageQueue 对象
    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
    if (messageQueue == NULL) {
        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
        return 0;
    }
    //(3)创建 NativeInputEventReceiver 对象
    sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
            receiverWeak, inputChannel, messageQueue);
    //(4)执行 NativeInputEventReceiver 对象 initialize() 方法
    status_t status = receiver->initialize();
    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); // 保留对象的引用
    return reinterpret_cast<jlong>(receiver.get());
}


// NativeInputEventReceiver 构造函数:将传递来的入参保存到对应的成员变量中。
// class NativeInputEventReceiver : public LooperCallback 继承自 LooperCallback,
// 并实现 LooperCallback 的 handleEvent() 方法;
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) {
}


// initialize() 方法:执行了 setFdEvents,其中  ALOOPER_EVENT_INPUT = 1 << 0,左移0位,结果为 1;
status_t NativeInputEventReceiver::initialize() {
    setFdEvents(ALOOPER_EVENT_INPUT);
    return OK;
}


// setFdEvents() 方法:根据获取 MessageQueue->getLooper(),然后调用 Looper 的 addFd()
void NativeInputEventReceiver::setFdEvents(int events) { // 传入的参数为 1<< 0 为 1
    if (mFdEvents != events) {  // mFdEvents 初始化的值为 0;
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {  // 所以执行此分支  
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(fd);
        }
    }
}

从 nativeInit() 开始,经过一系列对象转换(将 Java 对象转换成 native 层的 c++ 对象),并创建 NativeInputEventReceiver,然后执行了 Looper 的 addFd() 方法;

(3)Looper.cpp 中:addFd()

int Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {
    // // 这里的 callback 是传入的参数 NativeInputEventReceiver 
    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) {
    if (!callback.get()) {  
        if (! mAllowNonCallbacks) {
            ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
            return -1;
        }

        if (ident < 0) {
            ALOGE("Invalid attempt to set NULL callback with ident < 0.");
            return -1;
        }
    } else {
        ident = POLL_CALLBACK;
    }

    int epollEvents = 0;
    if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
    if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;

    { // acquire lock
        AutoMutex _l(mLock);
        // 封装成 Request
        Request request;
        request.fd = fd;
        request.ident = ident;
        request.callback = callback;
        request.data = data;

        struct epoll_event eventItem;
        memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
        eventItem.events = epollEvents;
        eventItem.data.fd = fd;

        ssize_t requestIndex = mRequests.indexOfKey(fd); // 查找当前是否保存过
        if (requestIndex < 0) {  // 如果没有则保存
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.add(fd, request);  // 保存 request 
        } else {  // 如果有则替换
            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
            if (epollResult < 0) {
                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
                return -1;
            }
            mRequests.replaceValueAt(requestIndex, request);
        }
    } // 释放锁
    return 1;
}

在 addFd() 方法中,先将参数封装成 Request,然后向保存 Request 的容器中查询是否保存过当前 fd,没有保存则保存,反之则替换;通过 epoll_ctl() 添加 fd 描述符后,当有数据从服务端写入,就会唤醒 Looper,最终回调到 NativeInputEventReceiver(继承自LooperCallback) 的 handleEvent() 方法。

(4)pollInner() 执行回调:

int Looper::pollInner(int timeoutMillis) {
    // ...
    // Invoke all response callbacks.
    for (size_t i = 0; i < mResponses.size(); i++) {
        Response& response = mResponses.editItemAt(i);
        if (response.request.ident == POLL_CALLBACK) {
            int fd = response.request.fd;
            int events = response.events;
            void* data = response.request.data;
            // 执行 回调函数 handleEvent()
            int callbackResult = response.request.callback->handleEvent(fd, events, data);
            if (callbackResult == 0) {
                removeFd(fd);
            }
            // Clear the callback reference in the response structure promptly because we
            // will not clear the response vector itself until the next poll.
            response.request.callback.clear();
            result = POLL_CALLBACK;
        }
    }
    return result;
}

还记得上面说到传入的 Looper 是主线程的 Looper 吗?在 Java 层主线程中,执行了 looper.loop() 方法后,会循环执行 pollInner(),所以这里的回调执行后就会返回给 Java 层处理了;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容