本篇博客对Android的窗口管理进行研究。其中会涉及到windowManagerService、windowManager、SurfaceFlinger等内容.

一、WindowManager简单介绍

首先这个WindowManager为Android的窗口管理器。在android中,存在一个WindowManager接口,该接口为windowManager提供的对外接口,我们可以通过Context.getSystemService(Context.WINDOW_SERVICE);来获得该对象。按照文档的说法,一种最为简便的在另外一个页面上展示window的方法是创建一个Presentation,Presentation会自动为了展示获得WindowManager和Context。

windowManager有两个方法,getDefaultDisplay和removeViewImmediate方法。我们主要来看常用的这个getDefaultDisplay方法。首先需要知道有两种display,一个是application display,一个是real display。real display包括状态栏。这两种的display在point和displayMetrics上自然不同。我们调用getSize、getRectSize、getMetrics来获得application的display,通过调用getRealSize、getRealMetrics来获得实际的的size和displayMetrics。

获得屏幕尺寸的方法包括三种:

1
2
3
4
5
//该方法已经在API level 13以后废弃。
WindowManager windowManager = getWindowManager();
Display display = windowManager.getDefaultDisplay();
int screenWidth = display.getWidth();
int screenHeight = display.getHeight();

上述方法已经废弃了,废弃以后使用下面的方法:

1
2
3
4
5

Point outSize = new Point();
display.getSize(outSize);
screenWidth = outSize.x;
screenHeight = outSize.y;

但该方法本质上还是调用metrics,所以我们直接使用第三种方法即可

1
2
3
4
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
screenWidth =dm.widthPixels;
screenHeight =dm.heightPixels;

二、WindowManager和Activity的关联

一个activity是如何和windowManager进行关联呢?
首先,让我们看Android的主线程activityThread,由于Android的启动和有关操作是通过消息机制来进行的,在ActivityThread类中的Handler对LAUNCH_ACTIVITY消息的处理就可以看做系统的启动activity的有关逻辑。该逻辑中关键的一行代码是handleLaunchActivity。在其中会调用performLaunchActivity方法。在该方法中主要完成了window和activity的关联。
我们主要来看这个方法,在这个方法中,会新建一个activity,并且会通过attach方法传入很多参数,其中就包括了代表activity标识的token,

1
2
3
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
activity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config);

在该attach方法中,会调用window的setWindowManager方法,将token设置给window,在window中会将token传给WindowManager.LayoutParams类型的wp对象。而当我们在调用ViewRootImpl对象的setView方法时会调用
mWindowAttributes.copyFrom(attrs);该方法就会将token传递给windowManager。
参考一篇博客的叙述:在WindowManager中会通过该token生成一个WindowToken对象,一个父窗口对应一个WindowToken,而具有相同token的所有其子窗口都会被归到一个WindowToken中。即如果token相同,表示activity都在一个窗口中。还有个用来标识窗口的类AppWindowToken,继承自WindowToken,它由activity传过来的token生成,和Activity一一对应。通过token,就能找到activity和window的对应关系了。[2]

根据之前的博客[3],我们知道在activity的setContentView方法中,会调用windowManager的addView方法,并且该方法会调用到ViewRootImpl 的setView方法。

那么这个setView除了之前博客中提到的performTraversals和onMeasure onLayout等方法,还在setView方法中调用到了IWindowSession,(ipc调用IwindowSession的add)IWindowSession是windowManager和windowManagerService之间通信的桥梁,其中IWindowSession是AIDL文件。这里介绍一下AIDL[4]。

AIDL,全称Android Interface Definition Language,Android接口定义语言。根据官方文档。On Android, one process cannot normally access the memory of another process.So to talk, they need to decompose their objects into primitives that the operating system can understand。Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。关于AIDL和Binder的介绍,会在后面继续研究,这里继续窗口管理方面的研究。

总结一下,在windowManager中调用到的setView,会调用到ViewRootImpl,该类会通过ipc调用IWindowSession的add方法,并最终调用到WindowManagerService的addView方法,而ViewRootImpl其实为handler,他也通过IWindow来接收WindowManagerService接收指令。下面来介绍WindowManagerService。

三、WindowManagerService

WindowManagerService实现了很多有关窗口的功能。按照该篇博客[5]的介绍。WindowManagerService主要完成的实现包括。
1.窗口大小和位置(X轴和Y轴)的计算过程。
2.窗口的组织方式。
3.输入法窗口的调整过程。
4.壁纸窗口的调整过程。
5.窗口Z轴位置的计算和调整过程。
6.Activity窗口的启动窗口的显示过程。
7.Activity窗口的切换过程。
8.Activity窗口的动画显示过程。

WindowManagerService源代码比较复杂,这里我们先研究一下窗口的创建过程。
首先,关于窗口数据的构建,该篇博客[2]总结的很好,可以作为参考。
“首先new一个WindowState类,该类表示一个窗口。结合WindowToken和AppWindowToken,完整的定义了一个窗口内容。接着创建一个管道,用于处理消息输入。再然后调用attach方法,创建和Surface相关的内容,用于和surfaceFlinger交互。这样,整个窗口就搭建完成了。有了WindowState类对窗口属性的保存以及token对窗口归属的标识,之后就可以通过SurfaceFlinger绘制在屏幕上了。之后通过InputManager,也能处理消息和WindowManagerService之间的传递。保证窗口显示内容和用户操作保持一致性。”
总结一下windowManagerService的操作
1.会new一个WindowState类,结合token定义一个窗口。
2.创建一个管道,处理消息输入
3.调用attach方法,创建surface相关内容,用来和surfaceFlinger进行交互。

四、SurfaceFlinger相关内容研究
参考[6]博客,Android应用程序连接到SurfaceFlinger服务时,会由SurfaceFlinger服务创建一个类型为Client的Binder对象,并通过该对象来通知SurfaceFlinger来绘制自己的ui。Android应用程序需要传递为数不少的信息给surfaceFlinger。该种数据传递是通过一个系统匿名共享内存来进行。该匿名共享内存实际上是SharedClient,它包含了31个SharedBufferStack。也就是共享缓冲区堆栈。由于一个SharedClient对应一个Android应用程序,而该应用程序可能包含多个窗口。所以我们需要多个SharedBufferStack。同时注意这个SharedBufferStack,他代表缓冲区不止一个,利用N个缓冲区来绘制UI。该SharedBufferStack中保存的只是用来描述UI元数据的,并不是真正的UI数据,真正的UI数据保存在ShareBufferStack对应的GraphicBuffer中。

参考文献

[1]http://developer.android.com/intl/zh-cn/reference/android/view/WindowManager.html
[2]http://www.cnblogs.com/noTice520/archive/2012/10/25/2738050.html
[3]http://andyken.github.io/2015/06/07/the-process-of-activity-setContentView.html
[4]http://developer.android.com/intl/zh-cn/guide/components/aidl.html
[5]http://blog.csdn.net/luoshengyang/article/details/8462738
[6]http://blog.csdn.net/luoshengyang/article/details/7846923