美文网首页
framework 学习笔记23. input输入事件番外6(事

framework 学习笔记23. input输入事件番外6(事

作者: 加个标志位 | 来源:发表于2021-03-10 23:58 被阅读0次

    上一节中讲到 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 层处理了;

    相关文章

      网友评论

          本文标题:framework 学习笔记23. input输入事件番外6(事

          本文链接:https://www.haomeiwen.com/subject/jrtqqltx.html