Android视图SurfaceView的实现原理分析

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Android视图SurfaceView的实现原理
分析
在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面。

由于拥有独立的绘图表面,因此SurfaceView的UI 就可以在一个独立的线程中进行绘制。

又由于不会占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应。

在本文中,我们就详细分析SurfaceView的实现原理。

在前面一文中提到,普通的Android控件,例如TextView、Button和CheckBox等,它们都是将自己的UI绘制在宿主窗口的绘图表面之上,这意味着它们的UI是在应用程序的主线程中进行绘制的。

由于应用程序的主线程除了要绘制UI之外,还需要及时地响应用户输入,否则的话,系统就会认为应用程序没有响应了,因此就会弹出一个ANR对话框出来。

对于一些游戏画面,或者摄像头预览、视频播放来说,它们的UI都比较复杂,而且要求能够进行高效的绘制,因此,它们的UI就不适合在应用程序的主线程中进行绘制。

这时候就必须要给那些需要复杂而高效UI的视图生成一个独立的绘图表面,以及使用一个独立的线程来绘制这些视图的UI。

在前面和这两个系统的文章中,我们主要分析了Android应用程序窗口是如何通过SurfaceFlinger服务来绘制自己的UI的。

一般来说,每一个窗口在SurfaceFlinger服务中都对应有一个Layer,用来描述它的绘图表面。

对于那些具有SurfaceView的窗口来说,每一个SurfaceView在SurfaceFlinger服务中还对应有一个独立的Layer或者LayerBuffer,用来单独描述它的绘图表面,以区别于它的宿主窗口的绘图表面。

无论是LayerBuffer,还是Layer,它们都是以LayerBase为基类的,也就是说,SurfaceFlinger服务把所有的LayerBuffer和Layer都抽象为LayerBase,因此就可以用统一的流程来绘制和合成它们的UI。

由于LayerBuffer的绘制和合成与Layer的绘制和合成是类似的,因此本文不打算对LayerBuffer的绘制和合成操作进行分析。

需要深入理解LayerBuffer 的绘制和合成操作的,可以参考和这两个系统的文章。

为了接下来可以方便地描述SurfaceView的实现原理分析,我们假设在一个Activity 窗口的视图结构中,除了有一个DecorView顶层视图之外,还有两个TextView控件,以及一个SurfaceView视图,这样该Activity窗口在SurfaceFlinger服务中就对应有两个Layer或者一个Layer的一个LayerBuffer,如图1所示:
在图1中,Activity窗口的顶层视图DecorView及其两个TextView控件的UI都是绘制在SurfaceFlinger服务中的同一个Layer上面的,而SurfaceView的UI是绘制在SurfaceFlinger 服务中的另外一个Layer或者LayerBuffer上的。

注意,用来描述SurfaceView的Layer或者LayerBuffer的Z轴位置是小于用来其宿主Activity窗口的Layer的Z轴位置的,但是前者会在后者的上面挖一个“洞”出来,以便它的UI可以对用户可见。

实际上,SurfaceView在其宿主Activity窗口上所挖的“洞”只不过是在其宿主Activity窗口上设置了一块透明区域。

从总体上描述了SurfaceView的大致实现原理之后,接下来我们就详细分析它的具体实现过程,包括它的绘图表面的创建过程、在宿主窗口上面进行挖洞的过程,以及绘制过程。

1. SurfaceView的绘图表面的创建过程
由于SurfaceView具有独立的绘图表面,因此,在它的UI内容可以绘制之前,我们首先要将它的绘图表面创建出来。

尽管SurfaceView不与它的宿主窗口共享同一个绘图表面,但是它仍然是属于宿主窗口的视图结构的一个结点的,也就是说,SurfaceView仍然是会参与到宿主窗口的某些执行流程中去。

从前面一文可以知道,每当一个窗口需要刷新UI时,就会调用ViewRoot类的成员函数performTraversals。

ViewRoot类的成员函数performTraversals在执行的过程中,如果发现当前窗口的绘图表面还没有创建,或者发现当前窗口的绘图表面已经失效了,那么就会请求WindowManagerService服务创建一个新的绘图表面,同时,它还会通过一系列的回调函数来让嵌入在窗口里面的SurfaceView有机会创建自己的绘图表面。

接下来,我们就从ViewRoot类的成员函数performTraversals开始,分析SurfaceView 的绘图表面的创建过程,如图2所示:
这个过程可以分为8个步骤,接下来我们就详细分析每一个步骤。

Step 1. ViewRoot.performTraversals
[java] view plain copy 在CODE上查看代码片派生到我的代码片public final class ViewRoot extends Handler implements ViewParent, View.AttachInfo.Callbacks {
......
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
......
final View.AttachInfo attachInfo = mAttachInfo;
final int viewVisibility = getHostVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
......
if (mFirst) {
......
if (!mAttached) {
host.dispatchAttachedToWindow(attachInfo, 0);
mAttached = true;
}
......
}
......
if (viewVisibilityChanged) {
......
host.dispatchWindowVisibilityChanged(viewVisibility);
......
}
......
mFirst = false;
......
}
......
}
这个函数定义在文件frameworks/base/core/Java/android/view/ViewRoot.java中。

ViewRoot类的成员函数performTraversals的详细实现可以参考和这两篇文章,这里我们只关注与SurfaceView的绘图表面的创建相关的逻辑。

我们首先分析在ViewRoot类的成员函数performTraversals中四个相关的变量host、attachInfo、viewVisibility和viewVisibilityChanged。

变量host与ViewRoot类的成员变量mView指向的是同一个DecorView对象,这
个DecorView对象描述的当前窗口的顶层视图。

变量attachInfo与ViewRoot类的成员变量mAttachInfo指向的是同一个AttachInfo 对象。

在Android系统中,每一个视图附加到它的宿主窗口的时候,都会获得一个AttachInfo 对象,用来描述被附加的窗口的信息。

变量viewVisibility描述的是当前窗口的可见性。

变量viewVisibilityChanged描述的是当前窗口的可见性是否发生了变化。

ViewRoot类的成员变量mFirst表示当前窗口是否是第一次被刷新UI。

如果是的话,那么它的值就会等于true,说明当前窗口的绘图表面还未创建。

在这种情况下,如果ViewRoot 类的另外一个成员变量mAttached的值也等于true,那么就表示当前窗口还没有将它的各个子视图附加到它的上面来。

这时候ViewRoot类的成员函数performTraversals就会从当前窗口的顶层视图开始,通知每一个子视图它要被附加到宿主窗口上去了,这是通过调用变量host所指向的一个DecorView对象的成员函数dispatchAttachedToWindow来实现的。

DecorView类的成员函数dispatchAttachedToWindow是从父类ViewGroup继承下来的,在后面的Step 2中,我们再详细分析ViewGroup类的成员数dispatchAttachedToWindow的实现。

接下来,ViewRoot类的成员函数performTraversals判断当前窗口的可见性是否发生了变化,即检查变量viewVisibilityChanged的值是否等于true。

如果发生了变化,那么就会从当前窗口的顶层视图开始,通知每一个子视图它的宿主窗口的可见发生变化了,这是通过调用变量host所指向的一个DecorView对象的成员函数dispatchWindowVisibilityChanged 来实现的。

DecorView类的成员函数dispatchWindowVisibilityChanged是从父类ViewGroup 继承下来的,在后面的Step 5中,我们再详细分析ViewGroup类的成员数dispatchWindowVisibilityChanged的实现。

我们假设当前窗口有一个SurfaceView,那么当该SurfaceView接收到它被附加到宿主窗口以及它的宿主窗口的可见性发生变化的通知时,就会相应地将自己的绘图表面创建出来。

接下来,我们就分别分析ViewGroup类的成员数dispatchAttachedToWindow和dispatchWindowVisibilityChanged的实现,以便可以了解SurfaceView的绘图表面的创建过程。

Step 2. ViewGroup.dispatchAttachedToWindow
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public abstract class ViewGroup extends View implements ViewParent, ViewManager { ......
// Child views of this ViewGroup
private View[] mChildren;
// Number of valid children in the mChildren array, the rest should be null or not
// considered as children
private int mChildrenCount;
......
@Override
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
super.dispatchAttachedToWindow(info, visibility);
visibility |= mViewFlags & VISIBILITY_MASK;
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
children[i].dispatchAttachedToWindow(info, visibility);
}
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/ViewGroup.java中。

ViewGroup类的成员变量mChildren保存的是当前正在处理的视图容器的子视图,而另外一个成员变量mChildrenCount保存的是这些子视图的数量。

ViewGroup类的成员函数dispatchAttachedToWindow的实现很简单,它只是简单地调用当前正在处理的视图容器的每一个子视图的成员函数dispatchAttachedToWindow,以便可以通知这些子视图,它们被附加到宿主窗口上去了。

当前正在处理的视图容器即为当前正在处理的窗口的顶层视图,由于前面我们当前正在处理的窗口有一个SurfaceView,因此这一步就会调用到该SurfaceView的成员函数dispatchAttachedToWindow。

由于SurfaceView类的成员函数dispatchAttachedToWindow是从父类View继承下来的,因此,接下来我们就继续分析View类的成员函数dispatchAttachedToWindow的实现。

Step 3. View.dispatchAttachedToWindow
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
......
AttachInfo mAttachInfo;
......
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
......
onAttachedToWindow();
......
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数dispatchAttachedToWindow首先将参数info所指向的一个AttachInfo对象保存在自己的成员变量mAttachInfo中,以便当前视图可以获得其所附加在的窗口的相关信息,接下来再调用另外一个成员函数onAttachedToWindow来让子类有机会处理它被附加到宿主窗口的事件。

前面我们已经假设了当前处理的是一个SurfaceView。

SurfaceView类重写了父类View的成员函数onAttachedToWindow,接下来我们就继续分析SurfaceView的成员函数onAttachedToWindow的实现,以便可以了解SurfaceView的绘图表面的创建过程。

Step 4. SurfaceView.onAttachedToWindow
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class SurfaceView extends View {
......
IWindowSession mSession;
......
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mParent.requestTransparentRegion(this);
mSession = getWindowSession();
......
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/SurfaceView.java中。

SurfaceView类的成员函数onAttachedToWindow做了两件重要的事。

第一件事情是通知父视图,当前正在处理的SurfaceView需要在宿主窗口的绘图表面上挖一个洞,即需要在宿主窗口的绘图表面上设置一块透明区域。

当前正在处理的SurfaceView的父视图保存在父类View的成员变量mParent中,通过调用这个成员变量mParent所指向的一个ViewGroup对象的成员函数requestTransparentRegion,就可以通知到当前正在处理的SurfaceView的父视图,当前正在处理的SurfaceView需要在宿主窗口的绘
图表面上设置一块透明区域。

在后面第2部分的内容中,我们再详细分析SurfaceView在宿主窗口的绘图表面的挖洞过程。

第二件事情是调用从父类View继承下来的成员函数getWindowSession来获得一个实现了IWindowSession接口的Binder代理对象,并且将该Binder代理对象保存在SurfaceView类的成员变量mSession中。

从前面这个系列的文章可以知道,在Android系统中,每一个应用程序进程都有一个实现了IWindowSession接口的Binder代理对象,这个Binder代理对象是用来与WindowManagerService服务进行通信的,View类的成员函数getWindowSession返回的就是该Binder代理对象。

在接下来的Step 8中,我们就可以看到,SurfaceView就可以通过这个实现了IWindowSession接口的Binder代理对象来请求WindowManagerService服务为自己创建绘图表面的。

这一步执行完成之后,返回到前面的Step 1中,即ViewRoot类的成员函数performTraversals中,我们假设当前窗口的可见性发生了变化,那么接下来就会调用顶层视图的成员函数dispatchWindowVisibilityChanged,以便可以通知各个子视图,它的宿主窗口的可见性发生化了。

窗口的顶层视图是使用DecorView类来描述的,而DecroView类的成员函数dispatchWindowVisibilityChanged是从父类ViewGroup类继承下来的,因此,接下来我们就继续分析GroupView类的成员函数dispatchWindowVisibilityChanged的实现,以便可以了解包含在当前窗口里面的一个SurfaceView的绘图表面的创建过程。

Step 5. ViewGroup.dispatchWindowVisibilityChanged
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public abstract class ViewGroup extends View implements ViewParent, ViewManager { ......
@Override
public void dispatchWindowVisibilityChanged(int visibility) {
super.dispatchWindowVisibilityChanged(visibility);
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
children[i].dispatchWindowVisibilityChanged(visibility);
}
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/ViewGroup.java中。

ViewGroup类的成员函数dispatchWindowVisibilityChanged的实现很简单,它只是简单地调用当前正在处理的视图容器的每一个子视图的成员函数
dispatchWindowVisibilityChanged,以便可以通知这些子视图,它们所附加在的宿主窗口的可见性发生变化了。

当前正在处理的视图容器即为当前正在处理的窗口的顶层视图,由于前面我们当前正在处理的窗口有一个SurfaceView,因此这一步就会调用到该SurfaceView的成员函数dispatchWindowVisibilityChanged。

由于SurfaceView类的成员函数dispatchWindowVisibilityChanged是从父类View继承下来的,因此,接下来我们就继续分析View类的成员函数dispatchWindowVisibilityChanged 的实现。

Step 6. View.dispatchWindowVisibilityChanged
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
......
public void dispatchWindowVisibilityChanged(int visibility) {
onWindowVisibilityChanged(visibility);
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/View.java中。

View类的成员函数dispatchWindowVisibilityChanged的实现很简单,它只是调用另外一个成员函数onWindowVisibilityChanged来让子类有机会处理它所附加在的宿主窗口的可见性变化事件。

前面我们已经假设了当前处理的是一个SurfaceView。

SurfaceView类重写了父类View的成员函数onWindowVisibilityChanged,接下来我们就继续分析SurfaceView的成员函数onWindowVisibilityChanged的实现,以便可以了解SurfaceView的绘图表面的创建过程。

Step 7. SurfaceView.onWindowVisibilityChanged
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class SurfaceView extends View {
......
boolean mRequestedVisible = false;
boolean mWindowVisibility = false;
boolean mViewVisibility = false;
.....
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mWindowVisibility = visibility == VISIBLE;
mRequestedVisible = mWindowVisibility && mViewVisibility;
updateWindow(false, false);
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/SurfaceView.java中。

SurfaceView类有三个用来描述可见性的成员变量mRequestedVisible、mWindowVisibility和mViewVisibility。

其中,mWindowVisibility表示SurfaceView的宿主窗口的可见性,mViewVisibility表示SurfaceView自身的可见性。

只有当mWindowVisibility和mViewVisibility的值均等于true的时候,mRequestedVisible的值才为true,表示SurfaceView 是可见的。

参数visibility描述的便是当前正在处理的SurfaceView的宿主窗口的可见性,因此,SurfaceView类的成员函数onWindowVisibilityChanged首先将它记录在成员变量mWindowVisibility,接着再综合另外一个成员变量mViewVisibility来判断当前正在处理的SurfaceView是否是可见的,并且记录在成员变量mRequestedVisible中。

最后,SurfaceView类的成员函数onWindowVisibilityChanged就会调用另外一个成员函数updateWindow来更新当前正在处理的SurfaceView。

在更新的过程中,如果发现当前正在处理的SurfaceView还没有创建绘图表面,那么就地请求WindowManagerService服务为它创建一个。

接下来,我们就继续分析SurfaceView类的成员函数updateWindow的实现,以便可以了解SurfaceView的绘图表面的创建过程。

Step 8. SurfaceView.updateWindow
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public class SurfaceView extends View {
......
final Surface mSurface = new Surface();
......
MyWindow mWindow;
.....
int mWindowType = youtParams.TYPE_APPLICATION_MEDIA;
......
int mRequestedType = -1;
......
private void updateWindow(boolean force, boolean redrawNeeded) { if (!mHaveFrame) {
return;
}
......
int myWidth = mRequestedWidth;
if (myWidth <= 0) myWidth = getWidth();
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
getLocationInWindow(mLocation);
final boolean creating = mWindow == null;
final boolean formatChanged = mFormat != mRequestedFormat;
final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
final boolean visibleChanged = mVisible != mRequestedVisible
|| mNewSurfaceNeeded;
final boolean typeChanged = mType != mRequestedType;
if (force || creating || formatChanged || sizeChanged || visibleChanged || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]
|| mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
......
try {
final boolean visible = mVisible = mRequestedVisible;
mLeft = mLocation[0];
mTop = mLocation[1];
mWidth = myWidth;
mHeight = myHeight;
mFormat = mRequestedFormat;
mType = mRequestedType;
......
// Places the window relative
mLayout.x = mLeft;
mLayout.y = mTop;
mLayout.width = getWidth();
mLayout.height = getHeight();
......
mLayout.memoryType = mRequestedType;
if (mWindow == null) {
mWindow = new MyWindow(this);
mLayout.type = mWindowType;
......
mSession.addWithoutInputChannel(mWindow, mLayout,
mVisible ? VISIBLE : GONE, mContentInsets);
}
......
mSurfaceLock.lock();
try {
......
final int relayoutResult = mSession.relayout(
mWindow, mLayout, mWidth, mHeight,
visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
mVisibleInsets, mConfiguration, mSurface);
......
} finally {
mSurfaceLock.unlock();
}
......
} catch (RemoteException ex) {
}
.....
}
}
......
}
这个函数定义在文件frameworks/base/core/java/android/view/SurfaceView.java中。

在分析SurfaceView类的成员函数updateWindow的实现之前,我们首先介绍一些相关的成员变量的含义,其中,mSurface、mWindow、mWindowType和mRequestedType这四个成员变量是最重要的。

SurfaceView类的成员变量mSurface指向的是一个Surface对象,这个Surface对象描述的便是SurfaceView专有的绘图表面。

对于一般的视图来说,例如,TextView或者Button,
它们是没有专有的绘图表面的,而是与专宿主窗口共享同一个绘图表面,因此,它们就不会像SurfaceView一样,有一个专门的类型为Surface的成员变量来描述自己的绘图表面。

在前面一文提到,每一个Activity窗口都关联有一个W对象。

这个W对象是一个实现了IWindow接口的Binder本地对象,它是用来传递给WindowManagerService服务的,以便WindowManagerService服务可以通过它来和它所关联的Activity窗口通信。

例如,WindowManagerService服务通过这个W对象来通知它所关联的Activity窗口的大小或者可见性发生变化了。

同时,这个W对象还用来在WindowManagerService服务这一侧唯一地标志一个窗口,也就是说,WindowManagerService服务会为这个W对象创建一个WindowState 对象。

SurfaceView类的成员变量mWindow指向的是一个MyWindow对象。

MyWindow 类是从BaseIWindow类继承下来的,后者与W类一样,实现了IWindow接口。

也就是说,每一个SurfaceView都关联有一个实现了IWindow接口的Binder本地对象,就如第一个Activity窗口都关联有一个实现了IWindow接口的W对象一样。

从这里我们就可以推断出,每一个SurfaceView在WindowManagerService服务这一侧都对应有一个WindowState对象。

从这一点来看,WindowManagerService服务认为Activity窗口和SurfaceView的地位是一样的,即认为它们都是一个窗口,并且具有绘图表面。

接下来我们就会通过SurfaceView类的成员函数updateWindow的实现来证实这个推断。

SurfaceView类的成员变量mWindowType描述的是SurfaceView的窗口类型,它的默认值等于TYPE_APPLICA TION_MEDIA。

也就是说,我们在创建一个SurfaceView的时候,默认是用来显示多媒体的,例如,用来显示视频。

SurfaceView还有另外一个窗口类型TYPE_APPLICATION_MEDIA_OVERLAY,它是用来在视频上面显示一个Overlay的,这个Overlay可以用来显示视字幕等信息。

我们假设一个Activity窗口嵌入有两个SurfaceView,其中一个SurfaceView的窗口类型为TYPE_APPLICATION_MEDIA,另外一个SurfaceView的窗口类型为TYPE_APPLICATION_MEDIA_OVERLAY,那么在WindowManagerService服务这一侧就会对应有三个WindowState对象,其中,用来描述SurfaceView的WindowState对象是附加在用来描述Activity窗口的WindowState对象上的。

从前面一文可以知道,如果一个WindowState对象所描述的窗口的类型为TYPE_APPLICATION_MEDIA或者TYPE_APPLICATION_MEDIA_OVERLAY,那么它就会位于它所附加在的窗口的下面。

也就是说,类型为TYPE_APPLICATION_MEDIA或者TYPE_APPLICATION_MEDIA_OVERLAY的窗口的Z轴位置是小于它所附加在的窗口的Z 轴位置的。

同时,如果一个窗口同时附加有类型为TYPE_APPLICATION_MEDIA和TYPE_APPLICATION_MEDIA_OVERLAY的两个窗口,那么类型为TYPE_APPLICATION_MEDIA_OVERLAY的窗口的Z轴大于类型为TYPE_APPLICATION_MEDIA的窗口的Z轴位置。

从上面的描述就可以得出一个结论:如果一个Activity窗口嵌入有两个类型分别为TYPE_APPLICATION_MEDIA和TYPE_APPLICATION_MEDIA_OVERLAY的SurfaceView,那么该Activity窗口的Z轴位置大于类型为TYPE_APPLICATION_MEDIA_OVERLAY的SurfaceView的Z轴位置,而类型为
TYPE_APPLICATION_MEDIA_OVERLAY的SurfaceView的Z轴位置又大于类型为TYPE_APPLICATION_MEDIA的窗口的Z轴位置。

注意,我们在创建了一个SurfaceView之后,可以调用它的成员函数setZOrderMediaOverlay、setZOrderOnTop或者setWindowType来修改该SurfaceView的窗口类型,也就是修改该SurfaceView的成员变量mWindowType的值。

SurfaceView类的成员变量mRequestedType描述的是SurfaceView的绘图表面的类型,一般来说,它的值可能等于SURFACE_TYPE_NORMAL,也可能等于SURFACE_TYPE_PUSH_BUFFERS。

当一个SurfaceView的绘图表面的类型等于SURFACE_TYPE_NORMAL的时候,就表示该SurfaceView的绘图表面所使用的内存是一块普通的内存。

一般来说,这块内存是由SurfaceFlinger服务来分配的,我们可以在应用程序内部自由地访问它,即可以在它上面填充任意的UI数据,然后交给SurfaceFlinger服务来合成,并且显示在屏幕上。

在这种情况下,SurfaceFlinger服务使用一个Layer对象来描述该SurfaceView的绘图表面。

当一个SurfaceView的绘图表面的类型等于SURFACE_TYPE_PUSH_BUFFERS的时候,就表示该SurfaceView的绘图表面所使用的内存不是由SurfaceFlinger服务分配的,因而我们不能够在应用程序内部对它进行操作。

例如,当一个SurfaceView是用来显示摄像头预览或者视频播放的时候,我们就会将它的绘图表面的类型设置为SURFACE_TYPE_PUSH_BUFFERS,这样摄像头服务或者视频播放服务就会为该SurfaceView绘图表面创建一块内存,并且将采集的预览图像数据或者视频帧数据源源不断地填充到该内存中去。

注意,这块内存有可能是来自专用的硬件的,例如,它可能是来自视频卡的。

在这种情况下,SurfaceFlinger服务使用一个LayerBuffer对象来描述该SurfaceView 的绘图表面。

从上面的描述就得到一个重要的结论:绘图表面类型为SURFACE_TYPE_PUSH_BUFFERS的SurfaceView的UI是不能由应用程序来控制的,而是由专门的服务来控制的,例如,摄像头服务或者视频播放服务,同时,SurfaceFlinger服务会使用一种特殊的LayerBuffer来描述这种绘图表面。

使用LayerBuffer来描述的绘图表面在进行渲染的时候,可以使用硬件加速,例如,使用copybit或者overlay来加快渲染速度,从而可以获得更流畅的摄像头预览或者视频播放。

注意,我们在创建了一个SurfaceView之后,可以调用它的成员函数getHolder获得一个SurfaceHolder对象,然后再调用该SurfaceHolder对象的成员函数setType来修改该SurfaceView的绘图表面的类型,即修改该SurfaceView的成员变量mRequestedType的值。

介绍完成SurfaceView类的成员变量mSurface、mWindow、mWindowType和mRequestedType的含义之后,我们再介绍其它几个接下来要用到的其它成员变量的含义:
--mHaveFrame,用来描述SurfaceView的宿主窗口的大小是否已经计算好了。

只有当宿主窗口的大小计算之后,SurfaceView才可以更新自己的窗口。

--mRequestedWidth,用来描述SurfaceView最后一次被请求的宽度。

--mRequestedHeight,用来描述SurfaceView最后一次被请求的高度。

--mRequestedFormat,用来描述SurfaceView最后一次被请求的绘图表面的像素格式。

--mNewSurfaceNeeded,用来描述SurfaceView是否需要新创建一个绘图表面。

--mLeft、mTop、mWidth、mHeight,用来描述SurfaceView上一次所在的位置以及大小。

--mFormat,用来描述SurfaceView的绘图表面上一次所设置的格式。

--mVisible,用来描述SurfaceView上一次被设置的可见性。

--mType,用来描述SurfaceView的绘图表面上一次所设置的类型。

--mUpdateWindowNeeded,用来描述SurfaceView是否被WindowManagerService 服务通知执行一次UI更新操作。

--mReportDrawNeeded,用来描述SurfaceView是否被WindowManagerService服务通知执行一次UI绘制操作。

--mLayout,指向的是一个youtParams对象,用来传递SurfaceView的布局参数以及属性值给WindowManagerService服务,以便WindowManagerService服务可以正确地维护它的状态。

理解了上述成员变量的含义的之后,接下来我们就可以分析SurfaceView类的成员函数updateWindow创建绘图表面的过程了,如下所示:
(1). 判断成员变量mHaveFrame的值是否等于false。

如果是的话,那么就说明现在还不是时候为SurfaceView创建绘图表面,因为它的宿主窗口还没有准备就绪。

(2). 获得SurfaceView当前要使用的宽度和高度,并且保存在变量myWidth和myHeight中。

注意,如果SurfaceView没有被请求设置宽度或者高度,那么就通过调用父类View的成员函数getWidth和getHeight来获得它默认所使用的宽度和高度。

(3). 调用父类View的成员函数getLocationInWindow来获得SurfaceView的左上角位置,并且保存在成员变量mLocation所描述的一个数组中。

(4). 判断以下条件之一是否成立:
--SurfaceView的绘图表面是否还未创建,即成员变量mWindow的值是否等
于null;
--SurfaceView的绘图表面的像素格式是否发生了变化,即成员变量mFormat 和mRequestedFormat的值是否不相等;
--SurfaceView的大小是否发生了变化,即变量myWidth和myHeight是否与成员变量mWidth和mHeight的值不相等;
--SurfaceView的可见性是否发生了变化,即成员变量mVisible和mRequestedVisible的值是否不相等,或者成员变量NewSurfaceNeeded的值是否等于true;
--SurfaceView的绘图表面的类型是否发生了变化,即成员变量mType和mRequestedType的值是否不相等;
--SurfaceView的位置是否发生了变化,即成员变量mLeft和mTop的值是否不等于前面计算得到的mLocation[0]和mLocation[1]的值;
--SurfaceView是否被WindowManagerService服务通知执行一次UI更新操作,即成员变量mUpdateWindowNeeded的值是否等于true;
--SurfaceView是否被WindowManagerService服务通知执行一次UI绘制操作,即成员变量mReportDrawNeeded的值是否等于true;
--SurfaceView类的成员函数updateWindow是否被调用者强制要求刷新或者绘制SurfaceView,即参数force或者redrawNeeded的值是否等于true。

只要上述条件之一成立,那么SurfaceView类的成员函数updateWindow就需要对SurfaceView的各种信息进行更新,即执行以下第5步至第7步操作。

(5). 将SurfaceView接下来要设置的可见性、位置、大小、绘图表面像素格式和类型分别记录在成员变量mVisible、mLeft、mTop、mWidth、mHeight、mFormat和mType,同时还会将这些信息整合到成员变量mLayout所指向的一个youtParams 对象中去,以便接下来可以传递给WindowManagerService服务。

(6). 检查成员变量mWindow的值是否等于null。

如果等于null的话,那么就说明该SurfaceView还没有增加到WindowManagerService服务中去。

在这种情况下,就会创建一个MyWindow对象保存在该成员变量中,并且调用成员变量mSession所描述的一个Binder 代理对象的成员函数addWithoutInputChannel来将该MyWindow对象传递给WindowManagerService服务。

在前面的Step 4中提到,SurfaceView类的成员变量mSession 指向的是一个实现了IWindowSession接口的Binder代理对象,该Binder代理对象引用的是运行在WindowManagerService服务这一侧的一个Session对象。

Session类的成员函数addWithoutInputChannel与另外一个成员函数add的实现是类似的,它们都是用来在WindowManagerService服务内部为指定的窗口增加一个WindowState对象,具体可以参考前面一文。

不过,Session类的成员函数addWithoutInputChannel只是在
WindowManagerService服务内部为指定的窗口增加一个WindowState对象,而Session类的成员函数add除了会在WindowManagerService服务内部为指定的窗口增加一个WindowState 对象之外,还会为该窗口创建一个用来接收用户输入的通道,具体可以参考一文。

(7). 调用成员变量mSession所描述的一个Binder代理对象的成员函数relayout来请求WindowManagerService服务对SurfaceView的UI进行布局。

从前面一文可以知道,WindowManagerService服务在对一个窗口进行布局的时候,如果发现该窗口的绘制表面还未创建,或者需要需要重新创建,那么就会为请求SurfaceFlinger服务为该窗口创建一个新的绘图表面,并且将该绘图表面返回来给调用者。

在我们这个情景中,WindowManagerService 服务返回来的绘图表面就会保存在成员变量mSurface。

注意,这一步由于可能会修改SurfaceView的绘图表面,即修改成员变量mSurface的指向的一个Surface对象的内容,因此,就需要在获得成员变量mSurfaceLock所描述的一个锁的情况下执行,避免其它线程同时修改该绘图表面的内容,这是因为我们可能会使用一个独立的线程来来绘制SurfaceView 的UI。

执行完成上述步骤之后,SurfaceView的绘图表面的创建操作就执行完成了,而当SurfaceView有了绘图表面之后,我们就可以使用独立的线程来绘制它的UI了,不过,在绘制之前,我们还需要在SurfaceView的宿主窗口上挖一个洞,以便绘制出来的UI不会被挡住。

2. SurfaceView的挖洞过程
SurfaceView的窗口类型一般都是TYPE_APPLICATION_MEDIA或者TYPE_APPLICATION_MEDIA_OVERLAY,也就是说,它的Z轴位置是小于其宿主窗口的Z位置的。

为了保证SurfaceView的UI是可见的,SurfaceView就需要在其宿主窗口的上面挖一个洞出来,实际上就是在其宿主窗口的绘图表面上设置一块透明区域,以便可以将自己显示出来。

从SurfaceView的绘图表面的创建过程可以知道,SurfaceView在被附加到宿主窗口之上的时候,会请求在宿主窗口上设置透明区域,而每当其宿主窗口刷新自己的UI的时候,就会将所有嵌入在它里面的SurfaceView所设置的透明区域收集起来,然后再通知WindowManagerService服务为其设置一个总的透明区域。

从SurfaceView的绘图表面的创建过程可以知道,SurfaceView在被附加到宿主窗口之上的时候,SurfaceView类的成员函数onAttachedToWindow就会被调用。

SurfaceView 类的成员函数onAttachedToWindow在被调用的期间,就会请求在宿主窗口上设置透明区域。

接下来,我们就从SurfaceView类的成员函数onAttachedToWindow开始,分析SurfaceView 的挖洞过程,如图3所示:。

相关文档
最新文档