Android应用程序窗口Activity的绘图表面Surface的创建过程分析.docx
- 文档编号:15957848
- 上传时间:2023-07-09
- 格式:DOCX
- 页数:44
- 大小:365.87KB
Android应用程序窗口Activity的绘图表面Surface的创建过程分析.docx
《Android应用程序窗口Activity的绘图表面Surface的创建过程分析.docx》由会员分享,可在线阅读,更多相关《Android应用程序窗口Activity的绘图表面Surface的创建过程分析.docx(44页珍藏版)》请在冰点文库上搜索。
Android应用程序窗口Activity的绘图表面Surface的创建过程分析
Android应用程序窗口(Activity)的绘图表面(Surface)的创建过程分析
在前文中,我们分析了应用程序窗口连接到WindowManagerService服务的过程。
在这个过程中,WindowManagerService服务会为应用程序窗口创建过一个到SurfaceFlinger服务的连接。
有了这个连接之后,WindowManagerService服务就可以为应用程序窗口创建绘图表面了,以便可以用来渲染窗口的UI。
在本文中,我们就详细分析应用程序窗口的绘图表面的创建过程。
从前面和这两个系列的文章可以知道,每一个在C++层实现的应用程序窗口都需要有一个绘图表面,然后才可以将自己的UI表现出来。
这个绘图表面是需要由应用程序进程请求SurfaceFlinger服务来创建的,在SurfaceFlinger服务内部使用一个Layer对象来描述,同时,SurfaceFlinger服务会返回一个实现了ISurface接口的Binder本地对象给应用程序进程,于是,应用程序进程就可以获得一个实现了ISurface接口的Binder代理对象。
有了这个实现了ISurface接口的Binder代理对象之后,在C++层实现的应用程序窗口就可以请求SurfaceFlinger服务分配图形缓冲区以及渲染已经填充好UI数据的图形缓冲区了。
对于在Java层实现的Android应用程序窗口来说,它也需要请求SurfaceFlinger服务为它创建绘图表面,这个绘图表面使用一个Surface对象来描述。
由于在Java层实现的Android应用程序窗口还要接受WindowManagerService服务管理,因此,它的绘图表面的创建流程就会比在C++层实现的应用程序窗口复杂一些。
具体来说,就是在在Java层实现的Android应用程序窗口的绘图表面是通过两个Surface对象来描述,一个是在应用程序进程这一侧创建的,另一个是在WindowManagerService服务这一侧创建的,它们对应于SurfaceFlinger服务这一侧的同一个Layer对象,如图1所示:
在应用程序进程这一侧,每一个应用程序窗口,即每一个Activity组件,都有一个关联的Surface对象,这个Surface对象是保在在一个关联的ViewRoot对象的成员变量mSurface中的,如图2所示:
图2的类关系图的详细描述可以参考前面一文的图6,这里我们只关注Surface类的实现。
在应用程序进程这一侧,每一个Java层的Surface对都对应有一个C++层的Surface对象,并且后者的地址值保存在前者的成员变量mNativeSurface中。
C++层的Surface类的实现以及作用可以参考前面这个系列的文章。
在WindowManagerService服务这一侧,每一个应用程序窗口,即每一个Activity组件,都有一个对应的WindowState对象,这个WindowState对象的成员变量mSurface同样是指向了一个Surface对象,如图3所示:
图3的类关系图的详细描述可以参考前面一文的图7,这里我们同样只关注Surface类的实现。
在WindowManagerService服务这一侧,每一个Java层的Surface对都对应有一个C++层的SurfaceControl对象,并且后者的地址值保存在前者的成员变量mSurfaceControl中。
C++层的SurfaceControl类的实现以及作用同样可以参考前面这个系列的文章。
一个应用程序窗口分别位于应用程序进程和WindowManagerService服务中的两个Surface对象有什么区别呢?
虽然它们都是用来操作位于SurfaceFlinger服务中的同一个Layer对象的,不过,它们的操作方式却不一样。
具体来说,就是位于应用程序进程这一侧的Surface对象负责绘制应用程序窗口的UI,即往应用程序窗口的图形缓冲区填充UI数据,而位于WindowManagerService服务这一侧的Surface对象负责设置应用程序窗口的属性,例如位置、大小等属性。
这两种不同的操作方式分别是通过C++层的Surface对象和SurfaceControl对象来完成的,因此,位于应用程序进程和WindowManagerService服务中的两个Surface对象的用法是有区别的。
之所以会有这样的区别,是因为绘制应用程序窗口是独立的,由应用程序进程来完即可,而设置应用程序窗口的属性却需要全局考虑,即需要由WindowManagerService服务来统筹安排,例如,一个应用程序窗口的Z轴坐标大小要考虑它到的窗口类型以及它与系统中的其它窗口的关系。
说到这里,另外一个问题又来了,由于一个应用程序窗口对应有两个Surface对象,那么它们是如何创建出来的呢?
简单地说,就是按照以下步骤来创建:
1.应用程序进程请求WindowManagerService服务为一个应用程序窗口创建一个Surface对象;
2.WindowManagerService服务请求SurfaceFlinger服务创建一个Layer对象,并且获得一个ISurface接口;
3.WindowManagerService服务将获得的ISurface接口保存在其内部的一个Surface对象中,并且将该ISurface接口返回给应用程序进程;
4.应用程序进程得到WindowManagerService服务返回的ISurface接口之后,再将其封装成其内部的另外一个Surface对象中。
那么应用程序窗口的绘图表面又是什么时候创建的呢?
一般是在不存在的时候就创建,因为应用程序窗口在运行的过程中,它的绘图表面会根据需要来销毁以及重新创建的,例如,应用程序窗口在第一次显示的时候,就会请求WindowManagerService服务为其创建绘制表面。
从前面一文可以知道,当一个应用程序窗口被激活并且它的视图对象创建完成之后,应用程序进程就会调用与其所关联的一个ViewRoot对象的成员函数requestLayout来请求对其UI进行布局以及显示。
由于这时候应用程序窗口的绘图表面尚未创建,因此,ViewRoot类的成员函数requestLayout就会请求WindowManagerService服务来创建绘图表面。
接下来,我们就从ViewRoot类的成员函数requestLayout开始,分析应用程序窗口的绘图表面的创建过程,如图4所示:
这个过程可以分为10个步骤,接下来我们就详细分析每一个步骤。
Step1.ViewRoot.requestLayout
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicfinalclassViewRootextendsHandlerimplementsViewParent,
View.AttachInfo.Callbacks{
......
booleanmLayoutRequested;
......
publicvoidrequestLayout(){
checkThread();
mLayoutRequested=true;
scheduleTraversals();
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。
ViewRoot类的成员函数requestLayout首先调用另外一个成员函数checkThread来检查当前线程是否就是创建当前正在处理的ViewRoot对象的线程。
如果不是的话,那么ViewRoot类的成员函数checkThread就会抛出一个异常出来。
ViewRoot类是从Handler类继承下来的,用来处理应用程序窗口的UI布局和渲染等消息。
由于这些消息都是与Ui相关的,因此它们就需要在UI线程中处理,这样我们就可以推断出当前正在处理的ViewRoot对象是要应用程序进程的UI线程中创建的。
进一步地,我们就可以推断出ViewRoot类的成员函数checkThread实际上就是用来检查当前线程是否是应用程序进程的UI线程,如果不是的话,它就会抛出一个异常出来。
通过了上述检查之后,ViewRoot类的成员函数requestLayout首先将其成员变量mLayoutRequested的值设置为true,表示应用程序进程的UI线程正在被请求执行一个UI布局操作,接着再调用另外一个成员函数scheduleTraversals来继续执行UI布局的操作。
Step2.ViewRoot.scheduleTraversals
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicfinalclassViewRootextendsHandlerimplementsViewParent,
View.AttachInfo.Callbacks{
......
booleanmTraversalScheduled;
......
publicvoidscheduleTraversals(){
if(!
mTraversalScheduled){
mTraversalScheduled=true;
sendEmptyMessage(DO_TRAVERSAL);
}
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。
ViewRoot类的成员变量mTraversalScheduled用来表示应用程序进程的UI线程是否已经调度了一个DO_TRAVERSAL消息。
如果已经调度了的话,它的值就会等于true。
在这种情况下,ViewRoot类的成员函数scheduleTraversals就什么也不做,否则的话,它就会首先将成员变量mTraversalScheduled的值设置为true,然后再调用从父类Handler继承下来的成员函数sendEmptyMessage来往应用程序进程的UI线程发送一个DO_TRAVERSAL消息。
这个类型为DO_TRAVERSAL的消息是由ViewRoot类的成员函数performTraversals来处理的,因此,接下来我们就继续分析ViewRoot类的成员函数performTraversals的实现。
Step3.ViewRoot.performTraversals
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicfinalclassViewRootextendsHandlerimplementsViewParent,
View.AttachInfo.Callbacks{
......
ViewmView;
......
booleanmLayoutRequested;
booleanmFirst;
......
booleanmFullRedrawNeeded;
......
privatefinalSurfacemSurface=newSurface();
......
privatevoidperformTraversals(){
......
finalViewhost=mView;
......
mTraversalScheduled=false;
......
booleanfullRedrawNeeded=mFullRedrawNeeded;
booleannewSurface=false;
......
if(mLayoutRequested){
......
host.measure(childWidthMeasureSpec,childHeightMeasureSpec);
.......
}
......
intrelayoutResult=0;
if(mFirst||windowShouldResize||insetsChanged
||viewVisibilityChanged||params!
=null){
......
booleanhadSurface=mSurface.isValid();
try{
......
relayoutResult=relayoutWindow(params,viewVisibility,insetsPending);
......
if(!
hadSurface){
if(mSurface.isValid()){
......
newSurface=true;
fullRedrawNeeded=true;
......
}
}
......
}catch(RemoteExceptione){
}
......
}
finalbooleandidLayout=mLayoutRequested;
......
if(didLayout){
mLayoutRequested=false;
......
host.layout(0,0,host.mMeasuredWidth,host.mMeasuredHeight);
......
}
......
mFirst=false;
......
booleancancelDraw=attachInfo.mTreeObserver.dispatchOnPreDraw();
if(!
cancelDraw&&!
newSurface){
mFullRedrawNeeded=false;
draw(fullRedrawNeeded);
......
}else{
......
//Tryagain
scheduleTraversals();
}
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/ViewRoot.java中。
ViewRoot类的成员函数performTraversals的实现是相当复杂的,这里我们分析它的实现框架,在以后的文章中,我们再详细分析它的实现细节。
在分析ViewRoot类的成员函数performTraversals的实现框架之前,我们首先了解ViewRoot类的以下五个成员变量:
--mView:
它的类型为View,但它实际上指向的是一个DecorView对象,用来描述应用程序窗口的顶级视图,这一点可以参考前面一文。
--mLayoutRequested:
这是一个布尔变量,用来描述应用程序进程的UI线程是否需要正在被请求执行一个UI布局操作。
--mFirst:
这是一个布尔变量,用来描述应用程序进程的UI线程是否第一次处理一个应用程序窗口的UI。
--mFullRedrawNeeded:
这是一个布尔变量,用来描述应用程序进程的UI线程是否需要将一个应用程序窗口的全部区域都重新绘制。
--mSurface:
它指向一个Java层的Surface对象,用来描述一个应用程序窗口的绘图表面。
注意,成员变量mSurface所指向的Surface对象在创建的时候,还没有在C++层有一个关联的Surface对象,因此,这时候它描述的就是一个无效的绘图表面。
另外,这个Surface对象在应用程序窗口运行的过程中,也会可能被销毁,因此,这时候它描述的绘图表面也会变得无效。
在上述两种情况中,我们都需要请求WindowManagerService服务来为当前正在处理的应用程序窗口创建有一个有效的绘图表面,以便可以在上面渲染UI。
这个创建绘图表面的过程正是本文所要关心的。
理解了上述五个成员变量之后,我们就可以分析ViewRoot类的成员函数performTraversals的实现框架了,如下所示:
1.将成员变量mView和mFullRedrawNeeded的值分别保存在本地变量host和fullRedrawNeeded中,并且将成员变量mTraversalScheduled的值设置为false,表示应用程序进程的UI线程中的DO_TRAVERSAL消息已经被处理了。
2.本地变量newSurface用来描述当前正在处理的应用程序窗口在本轮的DO_TRAVERSAL消息处理中是否新创建了一个绘图表面,它的初始值为false。
3.如果成员变量mLayoutRequested的值等于true,那么就表示应用程序进程的UI线程正在被请求对当前正在处理的应用程序窗口执行一个UI布局操作,因此,这时候就会调用本地变量host所描述的一个顶层视图对象的成员函数measure来测量位于各个层次的UI控件的大小。
4.如果当前正在处理的应用程序窗口的UI是第一次被处理,即成员变量mFirst的值等于true,或者当前正在处理的应用程序窗口的大小发生了变化,即本地变量windowShouldResize的值等于true,或者当前正在处理的应用程序窗口的边衬发生了变化,即本地变量insetsChanged的值等于true,或者正在处理的应用程序窗口的可见性发生了变化,即本地变量viewVisibilityChanged的值等于true,或者正在处理的应用程序窗口的UI布局参数发生了变化,即本地变量params指向了一个WindowManager.LayoutParams对象,那么应用程序进程的UI线程就会调用另外一个成员函数relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口。
WindowManagerService服务在重新布局系统中的所有窗口的过程中,如果发现当前正在处理的应用程序窗口尚未具有一个有效的绘图表面,那么就会为它创建一个有效的绘图表面,这一点是我们在本文中所要关注的。
5.应用程序进程的UI线程在调用ViewRoot类的成员函数relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口之前,会调用成员变量mSurface所指向的一个Surface对象的成员函数isValid来判断它描述的是否是一个有效的绘图表面,并且将结果保存在本地变量hadSurface中。
6.应用程序进程的UI线程在调用ViewRoot类的成员函数relayoutWindow来请求WindowManagerService服务重新布局系统中的所有窗口之后,又会继续调用成员变量mSurface所指向的一个Surface对象的成员函数isValid来判断它描述的是否是一个有效的绘图表面。
如果这时候成员变量mSurface所指向的一个Surface对象描述的是否是一个有效的绘图表面,并且本地变量hadSurface的值等于false,那么就说明WindowManagerService服务为当前正在处理的应用程序窗口新创建了一个有效的绘图表面,于是就会将本地变量newSurface和fullRedrawNeeded的值均修改为true。
7.应用程序进程的UI线程再次判断mLayoutRequested的值是否等于true。
如果等于的话,那么就说明需要对当前正在处理的应用程序窗口的UI进行重新布局,这是通过调用本地变量host所描述的一个顶层视图对象的成员函数layout来实现的。
在对当前正在处理的应用程序窗口的UI进行重新布局之前,应用程序进程的UI线程会将成员变量mLayoutRequested的值设置为false,表示之前所请求的一个UI布局操作已经得到处理了。
8.应用程序进程的UI线程接下来就要开始对当前正在处理的应用程序窗口的UI进行重新绘制了,不过在重绘之前,会先询问一下那些注册到当前正在处理的应用程序窗口中的TreeObserver,即调用它们的成员函数dispatchOnPreDraw,看看它们是否需要取消接下来的重绘操作,这个询问结果保存在本地变量cancelDraw中。
9.如果本地变量cancelDraw的值等于false,并且本地变量newSurface的值也等于false,那么就说明注册到当前正在处理的应用程序窗口中的TreeObserver不要求取消当前的这次重绘操作,并且当前正在处理的应用程序窗口也没有获得一个新的绘图表面。
在这种情况下,应用程序进程的UI线程就会调用ViewRoot类的成员函数draw来对当前正在处理的应用程序窗口的UI进行重绘。
在重绘之前,还会将ViewRoot类的成员变量mFullRedrawNeeded的值重置为false。
10.如果本地变量cancelDraw的值等于true,或者本地变量newSurface的值等于true,那么就说明注册到当前正在处理的应用程序窗口中的TreeObserver要求取消当前的这次重绘操作,或者当前正在处理的应用程序窗口获得了一个新的绘图表面。
在这两种情况下,应用程序进程的UI线程就不能对当前正在处理的应用程序窗口的UI进行重绘了,而是要等到下一个DO_TRAVERSAL消息到来的时候,再进行重绘,以便使得当前正在处理的应用程序窗口的各项参数可以得到重新设置。
下一个DO_TRAVERSAL消息需要马上被调度,因此,应用程序进程的UI线程就会重新执行ViewRoot类的成员函数scheduleTraversals。
这样,我们就分析完成ViewRoot类的成员函数performTraversals的实现框架了,接下来我们就继续分析ViewRoot类的成员函数relayoutWindow的实现,以便可以看到当前正在处理的应用程序窗口的绘图表面是如何创建的。
Step4.ViewRoot.relayoutWindow
[java]viewplaincopy在CODE上查看代码片派生到我的代码片
publicfinalclassViewRootextendsHandlerimplementsViewParent,
View.AttachInfo.Callbacks{
......
staticIWindowSessionsWindowSession;
......
finalWmWindow;
......
privatefinalSurfacemSurface=newSurface();
......
privateintrelayoutWindow(WindowManager.LayoutParam
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 应用程序 窗口 Activity 绘图 表面 Surface 创建 过程 分析