应用程序添加窗口时注册InputChannel源码分析

来源:互联网 发布:网络热血传奇沙巴克 编辑:程序博客网 时间:2024/06/05 22:45

     好长一段时间没有写博客了,因为现在在华为公司工作,华为的信息安全和网络安全管理非常严格,程序员使用的办公工具和环境中,尤其像百度云盘、360云盘,环境像CSDN、eoe这种的博客类的,非常敏感,只要被查到,就处罚,所以不敢在公司不敢写,等下班回家又懒了,不想动了。随着学习知识的不断增多,还是认为需要写出来,给自己积累一些东西,也方便以后忘记时候查阅。

     好了,我们进入主题,大家都知道,所有的应用都是以消息模型来驱动,Android中精典的消息驱动模型组成有Looper、MessageQueue、Handler,Looper从消息队列中不断的读取消息,然后交给Handler处理,我们在应用当中启动activity、service、broadcast等等的都是通过这种方式,将要处理的业务放在消息队列中来循环的,本博客要分析的不是这些消息循环,大家如果想了解Android精典的消息循环模型,请看老罗的Android之旅,讲解的非常好,向大家强力推荐!

     老罗的Android之旅Android应用程序消息处理机制(Looper、Handler)分析

     用户在与界面进行交互时,用户的输入也是通过消息循环模型来进行分发处理的,那么用户的消息是通过什么传递给应用程序的呢?就是以窗口为单位,在窗口创建时,注册了管道来作为消息传输的通道,我们本节就来分析这个过程。

     在此需要说明一下,本博客分析的源码版本为android-4.4.4_r1。

     分析的入口就是窗口的创建过程,我们以Activity窗口为例,一个Activity类会对应一个窗口,对应一个ViewRootImpl,在创建过程中,最终都会调用到ViewRootImpl.setView()方法中,此方法中开始给Activity创建窗口并注册InputChannel,其中的InputChannel就是将消息分发到应用程序的通道,也就是我们本节的分析对象。

     ViewRootImpl.setView()的源码如下,因为太长,所以只选取部分:

     



     此方法中最重要的就是mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mInputChannel)这句,这句代码的功能就是向WindowManagerService进行请求,为当前的activity添加窗口,添加窗口时,传入参数mInputChannel,此时的mInputChannel对象是上面刚创建好的,只是一个空壳,是调用构造函数创建的,内部没有文件描述符,大家可以看一下InputChannel类的源码:
     

     InputChannel也是实现Parcelable接口的,说明此管道对象也是要用于进程间通信传输的,大家看到mPtr这个类变量应该就非常熟悉了,Android当中好多地方JNI的都是用这样的形式。
     mWindowSession.addToDisplay()方法调用Session对象的addToDisplay方法,它的内部只是简单的调用WindowManagerService的addWindow()方法,源码如下:
     

     我们可以看到,通过InputChannel[] inputChannels = InputChannel.openInputChannelPair(name)打开了一对管道,底层是通过Linux的pipe机制实现的,有兴趣的读者可以自己分析。那么为什么要一次创建两个管道呢?因为pipe机制创建的管道是单向的,应用从管道中读取消息需要一条,而消息处理完成后,需要向系统回复一下消息的处理结果,这样系统才能继续分发下一条消息,这样应用方还需要一条,所以总共需要两条管道。此方法执行结束后,返回的管道对中已经有了对应的文件描述符,也就是真正意义上的管道了。它内部调用了nativeOpenInputChannelPair(name),源码如下:
     

     先创建两个InputChannel 对象serverChannel和clientChannel,然后调用InputTransport.cpp中的openInputChannelPair()为serverChannel和clientChannel赋值
接着调用android_view_InputChannel_createInputChannel()为serverChannel和clientChannel添加文件描述符,完成后将两个对象添加到channelPair数组中,返回到WindowManagerService中的InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);,这样两个监控InputChannel对象就创建好了
接着调用win.setInputChannel(inputChannels[0]),此时请注意,数组中的0保存的是serverChannelObj,方法内部只是简单的对类对象赋值;最后重点看这一句mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle),mInputManager对象是一个InputManagerService,内部调用nativeRegisterInputChannel(mPtr, inputChannel, inputWindowHandle, false),变量mPtr在Android的JNI调用中到处可见,大家要注意这种方式
前面几句就是根据传进来的参数取出inputChannel、inputWindowHandle对象,然后调用im->registerInputChannel(env, inputChannel, inputWindowHandle, monitor)注册inputchannel
内部先通过getDispatcher()获取C++屋的InputDispatcher对象,然后调用它的registerInputChannel()方法
这里先创建一个Connection连接对象,然后获取inputChannel对应的文件描述符,然后将对应的文件描述符添加到对应的Looper对象中,而添加的实质过程就是使用Linux的epoll机制去监控对应的事件,如果有事件发生,则会将事件分发出来进行处理,如果没有事件发生,则sleep,具体可参考老罗的Android之旅《Android应用程序消息处理机制(Looper、Handler)分析》
最后调用wake()让Looper进行循环
这样客户窗口的InputChannel就注册完成了,当InputDispatcher收到消息就会通过C++层的Looper分发出来

0 0
原创粉丝点击