在继续分析Dispatcher线程之前,我们先来讲解APP怎么与输入系统建立联系,他的核心就是scoketpair。在这之前我们先来回顾一下之前的知识: 1.InputRead线程从驱动读取到输入事件之后,稍作处理(比如来电的时候,按下音量键,就马上处理为静音),然后把这个输入事件放入到mInboundQueue队列中,然后唤醒Dispatcher线程。 2.Dispatcher线程从队列取出事件之后,也会稍作处理(如果为Global按键,或者System按键,处理完成之后,会直接抛弃),把user按键事件放入队列,然后查看目标APP,获得一个connection,然后把事件放入对应的onnection的队列中。
那么他是怎么查找,怎么放入connection的队列中的呢?这些就是该课时需要讲解的内容,前面提到过,他的核心是scoketpair。
我们可以想象到,pc机同时运行很多应用程序,一般只有最前面的应用程序能接受到输入事件,那么谁去告诉输入系统运行到屏幕最外面的应用程序是哪个呢?这里涉及到一个WindowManagerService(窗口管理服务),在其中,每一个应用程序都用一个WindowState结构体进行表示。
框架分析 上面是一个流程框图,看起来比较混乱,不过大家不要着急,慢慢为大家讲解:假设APP1,APP2已经存在,其中在WindowManagerService中都有对应的WindowState结构体对其进行描述,假设我们增加了一个APP3,那么APP3经过addToDisply,WindowManagerService收到后,为其创建一个新的WindowState与SocketPair,SocketPair会获得两个句柄fd0与fd1,其中fd1直接返回给应用程序,根据d0创建一个InputChannel,该InputChannel会放入到SocketPair中,并且通过registerInputChannel 注册到InputDisputcher,
在InputDisputcher.h文件中可以看到
KeyedVector mConnectionsByFd
定义,其中mConnectionsByFd中的Connections代表一个复数,表明KeyedVector中可能含有多个Connection。
通过registerInputChannel注册到InputDisputcher之后,InputDisputcher创建出一个Connection,把这个Connection放入到KeyedVector中。其中Connection包括:InputChannel以及fd。
这样,当Dispatcher线程获取到事件,会从根据屏幕最前面的应用程序,从KeyedVector中找出Connection中对应的fd,然后把事件放入其中,那么另外一个fd马上就能收到事件(应用程序)。
APP3在得到一个fd之后,会把他封装成一个InputChannel,然后再封装成windowInputEvenReceiver,最终把fd放入到Looper中,使用epoll进行查询等待。
我们要注意一点,框图中的InputRead线程与Dispatcher线程以及WindowManagerService线程他们处于SystemServer进程中,所以这三个进程之间是可以直接通信的,不需要通过binder。当需要跟APP1,APP2,APP3等等通信时,才需要使用binder实现进程之间的通信(或者使用事先建立的scoketpair)。我们要注意其中的一点差别,scoketpair中的fd是通过binder返回给应用程序APP的。
下面我们进入源码,验证以上的分析过程。
源码分析下图是源码中的详细调用过程: 对于应用程序,他肯定会调用自己对应的handleResumeActivity,其位于ActivityThread中,我们打开文件ActivityThread.java,他首先通过a.getWindowManager获得一个ViewManger wm,注意该WindowManager是本地的一个服务,不是系统service,然后通过wm.addView(decor,l)添加到本地的WindowManager服务中。
其中wm.addView方法定义于WindowManagerImpl.java文件,根据框图我们可以知道最终会调用到
mWindowSession.addToDisplay()
其是在ViewRootImpl.java中的root.setView方法中被调用,那么我们为什么认为addToDisplay()为一个远程调用呢?我们可以在ViewRootImpl.java中找到
mWindowSession = WindowManagerGlobal.getWindowSession();
sWindowSession = windowManager.openSession()/*他会调用到IWindowManager.java文件中的penSession,该文件是andriod编译过程中自动生成,其位于
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowManager.java*/
@Override public android.view.IWindowSession openSession(android.view.IWindowSessionCallback callback, com.android.internal.view.IInputMethodClient client, com.android.internal.view.IInputContext inputContext) throws android.os.RemoteExceptio
/*发起一个远程调用*/
mRemote.transact(Stub.TRANSACTION_openSession, _data, _reply, 0);
_reply.readException();
/*把返回结果转换为Stub.asInterface*/
_result = android.view.IWindowSession.Stub.asInterface(_reply.readStrongBinder());
可以知道mWindowSession是对远程服务的一个引用,其上的mWindowSession.addToDisplay()实际调用的为,SDKout/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/view/IWindowSession.java中的addToDisplay:
@Override public int addToDisplay(android.view.IWindow window, int seq, android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId, android.graphics.Rect outContentInsets, android.graphics.Rect outStableInsets, android.graphics.Rect outOutsets, android.view.InputChannel outInputChannel) throws android.os.RemoteException
mRemote.transact(Stub.TRANSACTION_addToDisplay, _data, _reply, 0);//发起远程调用
outOutsets.readFromParcel(_reply)//获取fd
通过远程调用,就实现了进程之间的通信(APP与WindowManagerService),APP在调用addToDisplay中进而调用outOutsets.readFromParcel(_reply);获得fd,
上述的框图讲解比较详细,就不在逐一讲解了。