[深入理解Android卷一全文-第八章]深入理解Surface系统

由于《深入理解Android 卷一》和《深入理解Android卷二》不再出版。而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容。

第8章  深入理解Surface系统

本章主要内容

·  具体分析一个Activity的显示过程。

·  具体分析Surface。

·  具体分析SurfaceFlinger。

本章涉及的源代码文件名称及位置:

· ActivityThread.java

framework/base/core/java/android/app/ActivityThread.java

·  Activity.java

framework/base/core/java/android/app/Activity.java

·  Instrumentation.java

framework/base/core/java/android/app/Instrumentation.java

·  PolicyManager.java

frameworks/policies/base/phone/com/android/internal/policy/impl/PolicyManager.java

·  Policy.java

frameworks/policies/base/phone/com/android/internal/policy/impl/Policy.java

·  PhoneWindow.java

frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java

·  Window.java

framework/base/core/java/android/view/Window.java

·  WindowManagerImpl

framework/ base/core/java/android/view/WindowManagerImpl.java

·  ViewRoot.java

framework/base/core/java/android/view/ViewRoot.java

·  Surface.java

framework/base/core/java/android/view/Surface.java

·  WindowManagerService.java

framework/base/services/java/com/android/server/WindowManagerService.java

·  IWindowSession.aidl

framework/base/core/java/android/view/IWindowSession.aidl

·  IWindow.aidl

framework/base/core/java/android/view/IWindow.aidl

·  SurfaceSession.java

framework/base/core/java/android/view/SurfaceSession.java

·  android_view_Surface.cpp

framework/base/core/jni/android_view_Surface.cpp

·  framebuffer_service.c

system/core/adb/framebuffer_service.c

·  SurfaceComposerClient.cpp

framework/base/libs/surfaceflinger_client/SurfaceComposerClient.cpp

·  SurfaceFlinger.cpp

framework/base/libs/surfaceflinger/SurfaceFlinger.cpp

·  ISurfaceComposer.h

framework/base/include/surfaceflinger/ISurfaceComposer.h

·  Layer.h

framework/base/include/surfaceflinger/Layer.h

·  Layer.cpp

framework/base/libs/surfaceflinger/Layer.cpp

·  LayerBase.cpp

framework/base/libs/surfaceflinger/LayerBase.cpp

·  Surface.cpp

framework/base/libs/surfaceflinger_client/Surface.cpp

·  SharedBufferStack.cpp

framework/base/libs/surfaceflinger_client/SharedBufferStack.cpp

·  GraphicBuffer.h

framework/base/include/ui/GraphicBuffer.h

·  GraphicBuffer.cpp

framework/base/libs/ui/GraphicBuffer.cpp

·  GraphicBufferAllocator.h

framework/base/include/ui/GraphicBufferAllocator.h

·  GraphicBufferAllocator.cpp

framework/base/libs/ui/GraphicBufferAllocator.cpp

·  GraphicBufferMapper.cpp

framework/base/libs/ui/GraphicBufferMapper.cpp

·  Android_natives.h

framework/base/include/ui/egl/Android_natives.h

·  android_native_buffer.h

framework/base/include/ui/android_native_buffer.h

·  native_handle.h

system/core/include/cutils/native_handle.h

·  gralloc.h

hardware/libhardware/include/hardware/gralloc.h

·  ISurface.cpp

framework/base/libs/surfaceflinger_client/ISurface.cpp

·  DisplayHardware.cpp

framework/base/libs/surfaceflinger/DisplayHardware.cpp

8.1概述

Surface是继Audio系统后要破解第二个复杂的系统。它的难度和复杂度远远超过了Audio。基于这样的情况。本章将集中精力打通Surface系统的“任督二脉”,这任督二脉各自是:

·  任脉:应用程序和Surface的关系。

·  督脉:Surface和SurfaceFlinger之间的关系。

当这二脉打通后,我们就能够自行修炼更高层次的功夫了。图8-1显示了这二脉的关系:

图8-1  Surface系统的任督二脉

当中,左图是任脉,右图是督脉。

·  先看左图。能够发现,不论是使用Skia绘制二维图像,还是用OpenGL绘制三维图像,终于Application都要和Surface交互。Surface就像是UI的画布。而App则像是在Surface上作画。

所以要想打通任脉。就须破解App和Surface之间的关系。

·  再看右图。

Surface和SurfaceFlinger的关系,非常像Audio系统中AudioTrack和AudioFlinger的关系。Surface向SurfaceFlinger提供数据,而SurfaceFlinger则混合数据。所谓打通督脉的关键。就是破解Surface和SurfaceFlinger之间的关系。

目标已清楚,让我们開始“运功”破解代码吧!

说明:为书写方便起见,后文将SurfaceFlinger简写为SF。

8.2一个Activity的显示

一般来说,应用程序的外表是通过Activity来展示的。那么,Activity是如何完毕界面绘制工作的呢?依据前面所讲的知识,应用程序的显示和Surface有关,那么具体到Activity上,它和Surface又是什么关系呢?

本节就来讨论这些问题。首先从Activity的创建说起。

8.2.1  Activity的创建

我们已经知道了Activity的生命周期,如onCreate、onDestroy等,但大家是否考虑过这样一个问题:

·  假设没有创建Activity,那么onCreate和onDestroy就没有不论什么意义。可这个Activity究竟是在哪里创建的?。

第4章中的“Zygote分裂”一节已讲过,Zygote在响应请求后会fork一个子进程,这个子进程是App相应的进程。它的入口函数是ActivityThread类的main函数。ActivityThread类中有一个handleLaunchActivity函数。它就是创建Activity的地方。一起来看这个函数,代码例如以下所看到的:

[-->ActivityThread.java]

private final voidhandleLaunchActivity(ActivityRecord r, Intent customIntent) {

//①performLaunchActivity返回一个Activity

Activitya = performLaunchActivity(r, customIntent);

if(a != null) {

r.createdConfig = new Configuration(mConfiguration);

Bundle oldState = r.state;

//②调用handleResumeActivity

handleResumeActivity(r.token, false, r.isForward);

}

......

}

handleLaunchActivity函数中列出了两个关键点,以下对其分别介绍。

1. 创建Activity

第一个关键函数performLaunchActivity返回一个Activity。这个Activity就是App中的那个Activity(仅考虑App中仅仅有一个Activity的情况),它是怎么创建的呢?其代码例如以下所看到的:

[-->ActivityThread.java]

private final ActivityperformLaunchActivity(ActivityRecord r,

Intent customIntent) {

ActivityInfo aInfo = r.activityInfo;

......//完毕一些准备工作

//Activity定义在Activity.java中

Activity activity = null;

try {

java.lang.ClassLoader cl = r.packageInfo.getClassLoader();

/*

mInstrumentation为Instrumentation类型,源文件为Instrumentation.java。

它在newActivity函数中依据Activity的类名通过Java反射机制来创建相应的Activity,

这个函数比較复杂。待会我们再分析它。

*/

activity = mInstrumentation.newActivity(

cl,component.getClassName(), r.intent);

r.intent.setExtrasClassLoader(cl);

if (r.state != null) {

r.state.setClassLoader(cl);

}

}catch (Exception e) {

......

}

try {

Application app =

r.packageInfo.makeApplication(false,mInstrumentation);

if (activity != null) {

//在Activity中getContext函数返回的就是这个ContextImpl类型的对象

ContextImpl appContext = new ContextImpl();

......

//以下这个函数会调用Activity的onCreate函数

mInstrumentation.callActivityOnCreate(activity, r.state);

......

return activity;

}

好了,performLaunchActivity函数的作用明确了吧?

·  依据类名以Java反射的方法创建一个Activity。

·  调用Activity的onCreate函数,開始SDK中大书特书Activity的生命周期。

那么。在onCreate函数中,我们通常会做什么呢?在这个函数中,和UI相关的重要工作就是调用setContentView来设置UI的外观。

接下去,须要看handleLaunchActivity中第二个关键函数handleResumeActivity。

2. 分析handleResumeActivity

上面已创建好了一个Activity,再来看handleResumeActivity。

它的代码例如以下所看到的:

[-->ActivityThread.java]

final void handleResumeActivity(IBinder token,boolean clearHide,

boolean isForward) {

boolean willBeVisible = !a.mStartedActivity;

if (r.window == null && !a.mFinished&& willBeVisible) {

r.window= r.activity.getWindow();

//①获得一个View对象

Viewdecor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

//②获得ViewManager对象

ViewManagerwm = a.getWindowManager();

......

//③把刚才的decor对象增加到ViewManager中

wm.addView(decor,l);

}

......//其它处理

}

上面有三个关键点。这些关键点似乎已经和UI部分(如View、Window)有联系了。那么这些联系是在什么时候建立的呢?在分析上面代码中的三个关键点之前。请大家想想在前面的过程中。哪些地方会和UI挂上钩呢?

·  答案就在onCreate函数中,Activity一般都在这个函数中通过setContentView设置UI界面。

看来,必须先分析setContentView。才干继续后面的征程。

3. 分析setContentView

setContentView有好几个同名函数,如今仅仅看当中的一个就能够了。

代码例如以下所看到的:

[-->Activity.java]

public void setContentView(View view) {

//getWindow返回的是什么呢?一起来看看。

getWindow().setContentView(view);

}

public Window getWindow() {

returnmWindow; //返回一个类型为Window的mWindow。它是什么?

}

上面出现了两个和UI有关系的类:View和Window[①]。来看SDK文档是怎么描写叙述这两个类的。这里先给出原文描写叙述。然后进行相应翻译:

·  Window:abstract base class for a top-levelwindow look and behavior policy. An instance of this class should be used asthe top-level view added to the window manager. It provides standard UIpolicies such as a background, title area, default key processing, etc.

中文的意思是:Window是一个抽象基类,用于控制顶层窗体的外观和行为。

做为顶层窗体它有什么特殊的职能呢?即绘制背景和标题栏、默认的按键处理等。

这里面有一句比較关键的话:它将做为一个顶层的view增加到Window Manager中。

·  View:This class represents the basicbuilding block for user interface components. A View occupies a rectangulararea on the screen and is responsible for drawing and event handling.

View的概念就比較简单了,它是一个主要的UI单元,占领屏幕的一块矩形区域,可用于绘制,并能处理事件。

从上面的View和Window的描写叙述。再加上setContentView的代码,我们能想象一下这三者的关系,如图8-2所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-2  Window/View的假想关系图

依据上面的介绍,大家可能会产生两个疑问:

·  Window是一个抽象类。它实际的对象究竟是什么类型?

·  Window Manager究竟是什么?

假设能有这样的疑问,就说明我们非常细心了。

以下试来解决这两个问题。

(1)Activity的Window

据上文解说可知,Window是一个抽象类。

它实际的对象究竟属于什么类型?先回到Activity创建的地方去看看。以下正是创建Activity时的代码,可当时没有深入地分析。

activity = mInstrumentation.newActivity(

cl,component.getClassName(), r.intent);

代码中调用了Instrumentation的newActivity。再去那里看看。

[-->Instrumentation.java]

public Activity newActivity(Class<?

>clazz, Context context,

IBinder token, Application application, Intent intent,

ActivityInfo info, CharSequencetitle, Activity parent,

String id,Object lastNonConfigurationInstance)

throws InstantiationException, IllegalAccessException{

Activity activity = (Activity)clazz.newInstance();

ActivityThread aThread = null;

//关键函数attach!!

activity.attach(context, aThread, this, token, application, intent,

info, title,parent, id, lastNonConfigurationInstance,

new Configuration());

return activity;

}

看到关键函数attach了吧?Window的真相立即就要揭晓了,让我们用咆哮体②来表达内心的激动之情吧!!

!!

[-->Activity.java]

final void attach(Context context,ActivityThread aThread,

Instrumentation instr, IBinder token, int ident,

Application application, Intent intent, ActivityInfo info,

CharSequence title, Activity parent, String id,

Object lastNonConfigurationInstance,

HashMap<String,Object> lastNonConfigurationChildInstances,

Configuration config) {

......

//利用PolicyManager来创建Window对象

mWindow = PolicyManager.makeNewWindow(this);

mWindow.setCallback(this);

......

//创建WindowManager对象

mWindow.setWindowManager(null, mToken, mComponent.flattenToString());

if(mParent != null) {

mWindow.setContainer(mParent.getWindow());

}

//保存这个WindowManager对象

mWindowManager = mWindow.getWindowManager();

mCurrentConfig = config;

}

此刻又有一点失望吧?这里冒出了个PolicyManager类,Window是由它的makeNewWindow函数所创建,因此还必须再去看看这个PolicyManager。

(2)水面下的冰山——PolicyManager

PolicyManager定义于PolicyManager.java文件,该文件在一个非常独立的文件夹下,现将其单独列出来:

·  frameworks/policies/base/phone/com/android/internal/policy/impl

注意,上面路径中的灰色文件夹phone是针对智能手机这样的小屏幕的;另外另一个平级的文件夹叫mid,是针对Mid设备的。

mid文件夹的代码比較少,可能眼下还没有开发完毕。

以下来看这个PolicyManager,它比較简单。

[-->PolicyManager.java]

public final class PolicyManager {

private static final String POLICY_IMPL_CLASS_NAME =

"com.android.internal.policy.impl.Policy";

private static final IPolicy sPolicy;

static{

//

try {

Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);

//创建Policy对象

sPolicy = (IPolicy)policyClass.newInstance();

}catch (ClassNotFoundException ex) {

......

}

private PolicyManager() {}

//通过Policy对象的makeNewWindow创建一个Window

publicstatic Window makeNewWindow(Context context) {

return sPolicy.makeNewWindow(context);

}

......

}

这里有一个单例的sPolicy对象。它是Policy类型。请看它的定义。

(3)真正的Window

Policy类型的定义代码例如以下所看到的:

[-->Policy.java]

public class Policy implements IPolicy {

private static final String TAG = "PhonePolicy";

private static final String[] preload_classes = {

"com.android.internal.policy.impl.PhoneLayoutInflater",

"com.android.internal.policy.impl.PhoneWindow",

"com.android.internal.policy.impl.PhoneWindow$1",

"com.android.internal.policy.impl.PhoneWindow$ContextMenuCallback",

"com.android.internal.policy.impl.PhoneWindow$DecorView",

"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState",

"com.android.internal.policy.impl.PhoneWindow$PanelFeatureState$SavedState",

};

static{

//载入全部的类

for (String s : preload_classes) {

try {

Class.forName(s);

} catch (ClassNotFoundException ex) {

......

}

}

}

public PhoneWindow makeNewWindow(Contextcontext) {

//makeNewWindow返回的是PhoneWindow对象

return new PhoneWindow(context);

}

......

}

至此,终于知道了代码:

mWindow = PolicyManager.makeNewWindow(this);

返回的Window,原来是一个PhoneWindow对象。它的定义在PhoneWindow.java中。

mWindow的真实身份搞清楚了。还剩下个WindowManager。

如今就来揭示其真面目。

(4)真正的WindowManager

先看WindowManager创建的代码,例如以下所看到的:

[-->Activity.java]

......//创建mWindow对象

//调用mWindow的setWindowManager函数

mWindow.setWindowManager(null, mToken,mComponent.flattenToString());

.....

上面的函数设置了PhoneWindow的WindowManager,只是第一个參数是null,这是什么意思?在回答此问题之前,先来看PhoneWindow的定义,它是从Window类派生。

[-->PhoneWindow.java::PhoneWindow定义]

public class PhoneWindow extends Windowimplements MenuBuilder.Callback

前面调用的setWindowManager函数。事实上是由PhoneWindow的父类Window类来实现的,来看其代码。例如以下所看到的:

[-->Window.java]

public void setWindowManager(WindowManagerwm,IBinder appToken, String appName) {     //注意。传入的wm值为null

mAppToken = appToken;

mAppName = appName;

if(wm == null) {

//假设wm为空的话,则创建WindowManagerImpl对象

wm = WindowManagerImpl.getDefault();

}

//mWindowManager是一个LocalWindowManager

mWindowManager = new LocalWindowManager(wm);

}

LocalWindowManager是在Window中定义的内部类,请看它的构造函数,其定义例如以下所看到的:

[-->Window.java::LocalWindowManager定义]

private class LocalWindowManager implementsWindowManager {

LocalWindowManager(WindowManager wm) {

mWindowManager = wm;//还好。仅仅是简单地保存了传入的wm參数

mDefaultDisplay = mContext.getResources().getDefaultDisplay(

mWindowManager.getDefaultDisplay());

}

......

如上面代码所看到的。LocalWindowManager将保存一个WindowManager类型的对象,这个对象的实际类型是WindowManagerImpl。

而WindowManagerImpl又是什么呢?来看它的代码,例如以下所看到的:

[-->WindowManagerImpl.java]

public class WindowManagerImpl implementsWindowManager {

......

public static WindowManagerImpl getDefault()

{

return mWindowManager; //返回的就是WindowManagerImpl对象

}

private static WindowManagerImpl mWindowManager= new WindowManagerImpl();

}

看到这里,是否有点头晕眼花?非常多朋友读我的一篇与此内容相关的博文后,普遍也有如此反应。对此,试配制了一剂治晕药方,如图8-3所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-3  Window和WindowManger的家族图谱

依据上图,可得出下面结论:

·  Activity的mWindow成员变量其真实类型是PhoneWindow。而mWindowManager成员变量的真实类型是LocalWindowManager。

·  LocalWindowManager和WindowManagerImpl都实现了WindowManager接口。

这里採用的是Proxy模式。表明LocalWindowManager将把它的工作托付WindowManagerImpl来完毕。

(5)setContentView的总结

了解了上述知识后,又一次回到setContentView函数。这次希望能分析得更深入些。

[-->Activity.java]

public void setContentView(View view) {

getWindow().setContentView(view);//getWindow返回的是PhoneWindow

}

一起来看PhoneWindow的setContentView函数,代码例如以下所看到的:

[-->PhoneWindow]

public void setContentView(View view) {

//调用另一个setContentView

setContentView(view,

new ViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));

}

public void setContentView(View view,ViewGroup.LayoutParams params) {

//mContentParent为ViewGroup类型,它的初值为null

if(mContentParent == null) {

installDecor();

}else {

mContentParent.removeAllViews();

}

//把view增加到ViewGroup中

mContentParent.addView(view, params);

......

}

mContentParent是一个ViewGroup类型。它从View中派生,所以也是一个UI单元。从它名字中“Group”所表达的意思分析,它还能够包括其它的View元素。这又是什么意思呢?

·  也就是说,在绘制一个ViewGroup时,它不仅须要把自己的样子画出来,还须要把它包括的View元素的样子也画出来。读者可将它想象成一个容器。容器中的元素就是View。

这里採用的是23种设计模式中的Composite模式,它是UI编程中经常使用的模式之中的一个。

再来看installDecor函数,其代码例如以下所看到的:

[-->PhoneWindow.java]

private void installDecor() {

if (mDecor == null) {

//创建mDecor。它为DecorView类型,从FrameLayout派生

mDecor= generateDecor();

......

}

if(mContentParent == null) {

//得到这个mContentParent

mContentParent = generateLayout(mDecor);

//创建标题栏

mTitleView= (TextView)findViewById(com.android.internal.R.id.title);

......

}

generateLayout函数的输入參数为mDecor,输出为mContentParent,代码例如以下所看到的:

[-->PhoneWindow]

protected ViewGroup generateLayout(DecorViewdecor){

......

intlayoutResource;

intfeatures = getLocalFeatures();

if((features & ((1 << FEATURE_LEFT_ICON) |(1 <<FEATURE_RIGHT_ICON))) != 0) {

if(mIsFloating) {

//依据情况取得相应标题栏的资源id

layoutResource =  com.android.internal.R.layout.dialog_title_icons;

}

......

}

mDecor.startChanging();

View in =mLayoutInflater.inflate(layoutResource, null);

//增加标题栏

decor.addView(in,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

/*

ID_ANDROID_CONTENT的值为”com.android.internal.R.id.content”

这个contentParent由findViewById返回。实际上就是mDecorView的一部分。

*/

ViewGroupcontentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

......

mDecor.finishChanging();

returncontentParent;

}

以下看findViewById是如何实现的。它定义在Window.java中,代码例如以下所看到的:

[-->Window.java]

public View findViewById(int id) {

//getDecorView将返回mDecorView,所以contentParent确实是DecorView的一部分

returngetDecorView().findViewById(id);

}

大家还记得图8-2吗?介绍完上面的知识后,依据图8-2。可绘制更仔细的图8-4:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-4  一个Activity中的UI组件

可从上图中看出,在Activity的onCreate函数中,通过setContentView设置的View。事实上仅仅是DecorView的子View。DecorView还处理了标题栏显示等一系列的工作。

注意,这里使用了设计模式中的Decorator(装饰)模式,它也是UI编程中经常使用的模式之中的一个。

4. 重回handleResumeActivity

看完setContentView的分析后。不知大家是否还记得这样一个问题:为什么要分析这个setContentView函数?在继续前行之前。先来回想一下被setContentView打断的流程。

当时,我们正在分析handleResumeActivity,代码例如以下所看到的:

[-->ActivityThread.java]

final void handleResumeActivity(IBinder token,boolean clearHide,

boolean isForward) {

booleanwillBeVisible = !a.mStartedActivity;

......

if (r.window == null && !a.mFinished&& willBeVisible) {

r.window= r.activity.getWindow();

//①获得一个View对象。如今知道这个view就是DecorView

Viewdecor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

//②获得ViewManager对象,这个wm就是LocalWindowManager

ViewManagerwm = a.getWindowManager();

WindowManager.LayoutParamsl = r.window.getAttributes();

a.mDecor= decor;

l.type =WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

if(a.mVisibleFromClient) {

a.mWindowAdded= true;

//③把刚才的decor对象增加到ViewManager中

wm.addView(decor,l);

}

......//其它处理

}

在上面的代码中,由于出现了多个之前不熟悉的东西,如View、ViewManager等,而这些东西的来源又和setContentView有关,所以我们才转而去分析setContentView了。想起来了吧?

由于代码比較长,跳转关系也非常多,在分析代码时。请读者把握流程。在大脑中建立一个代码分析的堆栈。

以下就从addView的分析開始。如前面所介绍的。它的调用方法是:

wm.addView(decor, l);//wm类型实际是LocalWindowManager

来看这个addView函数,它的代码例如以下所看到的:

[-->Window.javaLocalWindowManager]

public final void addView(View view,ViewGroup.LayoutParams params) {

WindowManager.LayoutParams wp =(WindowManager.LayoutParams)params;

CharSequence curTitle = wp.getTitle();

...... //做一些操作,能够无论它

//还记得前面提到过的Proxy模式吗?mWindowManager对象实际上是WindowManagerImpl类型

mWindowManager.addView(view, params);

}

看来,要搞清楚这个addView函数还是比較麻烦的,由于如今必须到WindowManagerImpl中去看看。

它的代码例如以下所看到的:

[-->WindowManagerImpl.java]

private void addView(View view,ViewGroup.LayoutParams params, boolean nest)

{

ViewRootroot; //ViewRoot。幕后的主角终于登场了!

synchronized(this) {

//①创建ViewRoot

root =new ViewRoot(view.getContext());

root.mAddNesting = 1;

view.setLayoutParams(wparams);

if(mViews == null) {

index = 1;

mViews = new View[1];

mRoots= new ViewRoot[1];

mParams = new WindowManager.LayoutParams[1];

} else{

......

}

index--;

mViews[index]= view;

mRoots[index]= root;//保存这个root

mParams[index]= wparams;

//②setView,当中view是刚才我们介绍的DecorView

root.setView(view,wparams, panelParentView);//

}

“ViewRoot,ViewRoot ....”。主角终于出场了!

即使没介绍它的真实身份,不禁也想欢呼几声。可为避免高兴得过早。还是应该先冷静地分析一下它。这里,列出了ViewRoot的两个重要关键点。

(1)ViewRoot是什么?

ViewRoot是什么?看起来好像和View有些许关系,至少名字非常像。事实上,它的确和View有关系,由于它实现了ViewParent接口。SDK的文档中有关于ViewParent的介绍。但它和Android基本画图单元中的View却不太一样,比方:ViewParent不处理绘画,由于它没有onDraw函数。

如上所述,ViewParent和绘画没有关系,那么,它的作用是什么?先来看它的代码。例如以下所看到的:

[-->ViewRoot.java::ViewRoot定义]

public final class ViewRoot extends Handlerimplements ViewParent,

View.AttachInfo.Callbacks //从Handler类派生

{

private final Surface mSurface = new Surface();//这里创建了一个Surface对象

final W mWindow; //这个是什么?

View mView;

}

上面这段代码传达出了一些重要信息:

·  ViewRoot继承了Handler类,看来它能处理消息。

ViewRoot果真重写了handleMessage函数。

稍侯再来看它。

·  ViewRoot有一个成员变量叫mSurface,它是Surface类型。

·  ViewRoot另一个W类型的mWindow和一个View类型的mView变量。

当中。W是ViewRoot定义的一个静态内部类:

static class W extends IWindow.Stub

这个类将參与Binder的通信,以后对此再做解说。先来介绍Surface类。

(2)神笔马良乎?

这里冒出来一个Surface类。它是什么?在回答此问题之前,先来考虑这样一个问题:

·  前文介绍的View、DecorView等都是UI单元,这些UI单元的绘画工作都在onDraw函数中完毕。假设把onDraw想象成画图过程,那么画布是什么?

Android肯定不是“马良”,它也没有那支能够在不论什么物体上作画的“神笔”,所以我们须要一块实实在在的画布,这块画布就是Surface。SDK文档对Surface类的说明是:Handle on to a raw buffer thatis being managed by the screen compositor。

这句话的意思是:

·  有一块Raw buffer。至于是内存还是显存。不必管它。

·  Surface操作这块Raw buffer。

·  Screen compositor(事实上就是SurfaceFlinger)管理这块Raw buffer。

Surface和SF、ViewRoot有什么关系呢?相信。聪明的你此时已经明确些了。这里用图8-5描绘一下心中的想法:

图8-5  马良的神笔工作原理

结合之前所讲的知识,图8-5清楚地传达了例如以下几条信息:

·  ViewRoot有一个成员变量mSurface。它是Surface类型,它和一块Raw Buffer有关联。

·  ViewRoot是一个ViewParent,它的子View的绘画操作,是在画布Surface上展开的。

·  Surface和SurfaceFlinger有交互,这非常相似AudioTrack和AudioFlinger之间的交互。

既然本章题目为“深入理解Surface系统”,那么就须要重点关注Surface和SurfaceFlinger间的关系。建立这个关系需ViewRoot的參与,所以应先来分析ViewRoot的创建和它的setView函数。

(3)ViewRoot的创建和对setView的分析

来分析ViewRoot的构造。关于它所包括内容,代码例如以下所看到的:

[-->ViewRoot.java]

public ViewRoot(Context context) {

super();

....

// getWindowSession?我们进去看看

getWindowSession(context.getMainLooper());

......//ViewRoot的mWindow是一个W类型,注意它不是Window类型,而是IWindow类型

mWindow= new W(this, context);

}

getWindowsession函数,将建立Activity的ViewRoot和WindowManagerService的关系。代码例如以下所看到的:

[-->ViewRoot.java]

ublic static IWindowSessiongetWindowSession(Looper mainLooper) {

synchronized (mStaticInit) {

if(!mInitialized) {

try {

InputMethodManagerimm =

InputMethodManager.getInstance(mainLooper);

//以下这个函数先得到WindowManagerService的Binder代理,然后调用它的openSession

sWindowSession = IWindowManager.Stub.asInterface(

ServiceManager.getService("window"))

.openSession(imm.getClient(), imm.getInputContext());

mInitialized = true;

} catch (RemoteException e) {

}

}

return sWindowSession;

}

}

WindowSession?WindowManagerService?第一次看到这些东西时,我快疯了。

复杂,太复杂,无比复杂!

要攻克这些难题,应先来回想一下与Zygote相关的知识:

·  WindowManagerService(以后简称WMS)由System_Server进程启动。SurfaceFlinger服务也在这个进程中。

看来,Activity的显示还不单纯是它自己的事,还须要和WMS建立联系才行。继续看。先看setView的处理。这个函数非常复杂,注意当中关键的几句。

openSession的操作是一个使用Binder通信的跨进程调用,暂且记住这个函数。在精简流程之后再来分析。

代码例如以下所看到的:

[-->ViewRoot.java]

public void setView(View view, WindowManager.LayoutParamsattrs,

View panelParentView){//第一个參数view是DecorView

......

mView= view;//保存这个view

synchronized (this) {

requestLayout(); //待会先看看这个。

try {

//调用IWindowSession的add函数,第一个參数是mWindow

res =sWindowSession.add(mWindow, mWindowAttributes,

getHostVisibility(), mAttachInfo.mContentInsets);

}

......

}

ViewRoot的setView函数做了三件事:

·  保存传入的view參数为mView,这个mView指向PhoneWindow的DecorView。

·  调用requestLayout。

·  调用IWindowSession的add函数,这是一个跨进程的Binder通信,第一个參数是mWindow,它是W类型,从IWindow.stub派生。

先来看这个requestLayout函数,它非常easy,就是往handler中发送了一个消息。注意。ViewRoot是从Handler派生的,所以这个消息最后会由ViewRoot自己处理。代码例如以下所看到的:

[-->ViewRoot.java]

public void requestLayout() {

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

public void scheduleTraversals() {

if(!mTraversalScheduled) {

mTraversalScheduled = true;

sendEmptyMessage(DO_TRAVERSAL); //发送DO_TRAVERSAL消息

}

}

好。requestLayout分析完毕。

从上面的代码中可发现,ViewRoot和远端进程SystemServer的WMS有交互,先来总结一下它和WMS的交互流程:

·  ViewRoot调用openSession,得到一个IWindowSession对象。

·  调用WindowSession对象的add函数,把一个W类型的mWindow对象做为參数传入。

5. ViewRoot和WMS的关系

上面总结了ViewRoot和WMS的交互流程,当中一共同拥有两个跨进程的调用。一起去看。

(1)调用流程分析

WMS的代码在WindowManagerService.java中:

[-->WindowManagerService.java]

public IWindowSessionopenSession(IInputMethodClient client,

IInputContextinputContext) {

......

return new Session(client, inputContext);

}

Session是WMS定义的内部类。它支持Binder通信。而且属于Bn端,即响应请求的服务端。

再来看它的add函数。代码例如以下所看到的:

[-->WindowManagerService.java::Session]

public int add(IWindow window,WindowManager.LayoutParams attrs,

int viewVisibility, Rect outContentInsets) {

//调用外部类对象的addWindow,也就是WMS的addWindow

returnaddWindow(this, window, attrs, viewVisibility,

outContentInsets);

}

[-->WindowManagerService.java]

public int addWindow(Session session, IWindowclient,

WindowManager.LayoutParams attrs, int viewVisibility,

Rect outContentInsets) {

......

//创建一个WindowState

win = new WindowState(session, client, token,

attachedWindow, attrs,viewVisibility);

......

//调用attach函数

win.attach();

......

return res;

}

WindowState类也是在WMS中定义的内部类,直接看它的attach函数。代码例如以下所看到的:

[-->WMS.java::WindowState]

void attach() {

//mSession就是Session对象。调用它的windowAddedLocked函数

mSession.windowAddedLocked();

}

[-->WMS.java::Session]

void windowAddedLocked() {

if(mSurfaceSession == null) {

......

//创建一个SurfaceSession对象

mSurfaceSession= new SurfaceSession();

......

}

mNumWindow++;

}

这里出现了另外一个重要的对象SurfaceSession。在解说它之前,急需理清一下现有的知识点。否则可能会头晕。

(2)ViewRoot和WMS的关系梳理

ViewRoot和WMS之间的关系,可用图8-6来表示:

图8-6  ViewRoot和WMS的关系

总结一下图8-6中的知识点:

·  ViewRoot通过IWindowSession和WMS进程进行跨进程通信。

IWindowSession定义在IWindowSession.aidl文件里。

这个文件在编译时由aidl工具处理。最后会生成相似于Native Binder中Bn端和Bp端的代码,后文会介绍它。

·  ViewRoot内部有一个W类型的对象,它也是一个基于Binder通信的类,W是IWindow的Bn端。用于响应请求。IWindow定义在另一个aidl文件IWindow.aidl中。

为什么须要这两个特殊的类呢?简介一下:

首先。来看IWindowSession.aidl对自己的描写叙述:

·  System private per-application interface to the window manager:也就是说每一个App进程都会和WMS建立一个IWindowSession会话。这个会话被App进程用于和WMS通信。

后面会介绍它的requestLayout函数。

再看对IWindow.adil的描写叙述:

·  API back to a client window that the Window Manager uses to informit of interesting things happening:这句话的大意是IWindow是WMS用来做事件通知的。每当发生一些事情时。WMS就会把这些事告诉某个IWindow。能够把IWindow想象成一个回调函数。

IWindow的描写叙述表达了什么意思呢?最好还是看看它的内容。代码例如以下所看到的:

[-->IWindow.aidl定义]

void dispatchKey(in KeyEvent event);

void dispatchPointer(in MotionEvent event, longeventTime,

boolean callWhenDone);

void dispatchTrackball(in MotionEvent event,long eventTime,

boolean callWhenDone);

明确了?这里的事件指的就是按键、触屏等事件。

那么,一个按键事件是如何被分发的呢?以下是它大致的流程:

·  WMS所在的SystemServer进程接收到按键事件。

·  WMS找到UI位于屏幕顶端的进程所相应的IWindow对象,这是一个Bp端对象。

·  调用这个IWindow对象的dispatchKey。IWindow对象的Bn端位于ViewRoot中。ViewRoot再依据内部View的位置信息找到真正处理这个事件的View,最后调用dispatchKey函数完毕按键的处理。

事实上这些按键事件的分发机制能够拿Windows的UI编程来做类比,在Windows中应用程序的按键处理流程是:

·  每一个按键事件都会转化成一个消息,这个消息将由系统增加到相应进程的消息队列中。该进程的消息在派发处理时,会依据消息的句柄找到相应的Window(窗体),继而该消息就由这个Window处理了。

注意:上面的描写叙述实际上大大简化了真实的处理流程,读者可在了解大体知识后进行更深入的研究。

上面介绍的是ViewRoot和WMS的交互,可是我们最关心的Surface还没有正式介绍。在此之前,还是先介绍Activity的流程。

8.2.2  Activity的UI绘制

ViewRoot的setView函数中,会有一个requestLayout。依据前面的分析可知,它会向ViewRoot发送一个DO_TRAVERSAL消息,来看它的handleMessage函数,代码例如以下所看到的:

[-->ViewRoot.java]

public void handleMessage(Message msg) {

switch (msg.what) {

......

case DO_TRAVERSAL:

......

performTraversals();//调用performTraversals函数

......

break;

......

}

}

再去看performTraversals函数,这个函数比較复杂。先仅仅看它的关键部分。代码例如以下所看到的:

[-->ViewRoot.java]

private void performTraversals() {

finalView host = mView;//还记得这mView吗?它就是DecorView喔

booleaninitialized = false;

booleancontentInsetsChanged = false;

booleanvisibleInsetsChanged;

try {

relayoutResult= //①关键函数relayoutWindow

relayoutWindow(params, viewVisibility,insetsPending);

}

......

draw(fullRedrawNeeded);// ②開始绘制

......

}

1. relayoutWindow的分析

performTraversals函数比較复杂,临时仅仅关注当中的两个函数relayoutWindow和draw就可以。

先看第一个relayoutWindow,代码例如以下所看到的:

[-->ViewRoot.java]

private intrelayoutWindow(WindowManager.LayoutParams params,

int viewVisibility, boolean insetsPending)throws RemoteException {

//原来是调用IWindowSession的relayOut,暂且记住这个调用

int relayoutResult = sWindowSession.relayout(

mWindow, params,

(int) (mView.mMeasuredWidth * appScale + 0.5f),

(int) (mView.mMeasuredHeight * appScale + 0.5f),

viewVisibility, insetsPending, mWinFrame,

mPendingContentInsets, mPendingVisibleInsets,

mPendingConfiguration, mSurface); mSurface做为參数传进去了。

}

......

}

relayoutWindow中会调用IWindowSession的relayout函数,暂且记住这个调用,在精简流程后再进行分析。

2. draw的分析

再来看draw函数。

这个函数非常重要,它可是Acitivity美丽脸蛋的塑造大师啊。代码例如以下所看到的:

[-->ViewRoot.java]

private void draw(boolean fullRedrawNeeded) {

Surface surface = mSurface;//mSurface是ViewRoot的成员变量

......

Canvascanvas;

try {

int left = dirty.left;

int top = dirty.top;

int right = dirty.right;

int bottom = dirty.bottom;

//从mSurface中lock一块Canvas

canvas = surface.lockCanvas(dirty);

......

mView.draw(canvas);//调用DecorView的draw函数,canvas就是画布的意思啦!

......

//unlock画布,屏幕上立即就会见到美丽宝贝的长相了。

surface.unlockCanvasAndPost(canvas);

}

......

}

UI的显示好像非常easy嘛!

真的是这样的吗?在揭露这个“惊天秘密”之前我们先总结一下Activity的显示流程。

8.2.3  Activity总结

不得不承认的是前面几节的内容非常多也非常繁杂,为了让后面分析的过程更流畅轻松一些。所以我们必须要总结一下。关于Activity的创建和显示。前面几节的信息可提炼成例如以下几条:

·  Activity的顶层View是DecorView,而我们在onCreate函数中通过setContentView设置的View仅仅只是是这个DecorView中的一部分罢了。DecorView是一个FrameLayout类型的ViewGroup。

·  Activity和UI有关,它包括一个Window(真实类型是PhoneWindow)和一个WindowManager(真实类型是LocalWindowManager)对象。这两个对象将控制整个Activity的显示。

·  LocalWindowManager使用了WindowManagerImpl做为终于的处理对象(Proxy模式)。这个WindowManagerImpl中有一个ViewRoot对象。

·  ViewRoot实现了ViewParent接口。它有两个重要的成员变量,一个是mView。它指向Activity顶层UI单元的DecorView。另外有一个mSurface。这个Surface包括了一个Canvas(画布)。除此之外,ViewRoot还通过Binder系统和WindowManagerService进行了跨进程交互。

·  ViewRoot能处理Handler的消息,Activity的显示就是由ViewRoot在它的performTraversals函数中完毕的。

·  整个Activity的画图流程就是从mSurface中lock一块Canvas,然后交给mView去自由发挥画画的才干,最后unlockCanvasAndPost释放这块Canvas。

这里和显示有关的就是最后三条了。当中最重要的内容都和Surface相关。既然mSurface是ViewRoot的本地变量,那就直接去看Surface。上面的代码分析一路走下来,真是比較流畅,波澜不惊,可事实果真如此吗?

8.3初识Surface

本节将介绍Surface对象。它可是纵跨Java/JNI层的对象。想必读者朋友已经摩拳擦掌。跃跃欲试了。

8.3.1和Surface有关的流程总结

这里。先总结一下前面解说中和Surface有关的流程:

·  在ViewRoot构造时。会创建一个Surface,它使用无參构造函数,代码例如以下所看到的:

private final Surface mSurface = new Surface();

·  ViewRoot通过IWindowSession和WMS交互,而WMS中会调用的一个attach函数,会构造一个SurfaceSession,代码例如以下所看到的:

void windowAddedLocked() {

if(mSurfaceSession == null) {

mSurfaceSession = new SurfaceSession();

mNumWindow++;

}

}

·  ViewRoot在performTransval的处理过程中会调用IWindowSession的relayout函数。

这个函数还没有分析。

·  ViewRoot调用Surface的lockCanvas,得到一块画布。

·  ViewRoot调用Surface的unlockCanvasAndPost释放这块画布。

这里从relayout函数開始分析,来看。

8.3.2  Surface之乾坤大挪移

1. 乾坤大挪移的表象

relayout的函数是一个跨进程的调用。由WMS完毕实际处理。先到ViewRoot中看看调用方的使用方法。代码例如以下所看到的:

[-->ViewRoot.java]

private intrelayoutWindow(WindowManager.LayoutParams params,

int viewVisibility, boolean insetsPending)

throws RemoteException {

int relayoutResult = sWindowSession.relayout(

mWindow, params,

(int) (mView.mMeasuredWidth * appScale + 0.5f),

(int) (mView.mMeasuredHeight * appScale + 0.5f),

viewVisibility, insetsPending, mWinFrame,

mPendingContentInsets, mPendingVisibleInsets,

mPendingConfiguration, mSurface);//mSurface传了进去

......

return relayoutResult;

}

再看接收方的处理。它在WMS的Session中。代码例如以下所看到的:

[-->WindowManagerService.java::Session]

public int relayout(IWindow window,WindowManager.LayoutParams attrs,

int requestedWidth, int requestedHeight, int viewFlags,

boolean insetsPending, Rect outFrame, Rect outContentInsets,

Rect outVisibleInsets, Configuration outConfig,

Surface outSurface) {

//注意最后这个參数的名字,叫outSurface

//调用外部类对象的relayoutWindow

returnrelayoutWindow(this, window, attrs,

requestedWidth,requestedHeight, viewFlags, insetsPending,

outFrame, outContentInsets,outVisibleInsets, outConfig,

outSurface);

}

[-->WindowManagerService.java]

public int relayoutWindow(Session session,IWindow client,

WindowManager.LayoutParams attrs, int requestedWidth,

int requestedHeight, int viewVisibility, boolean insetsPending,

Rect outFrame, Rect outContentInsets, Rect outVisibleInsets,

Configuration outConfig, SurfaceoutSurface){

.....

try {

//win就是WinState,这里将创建一个本地的Surface对象

Surfacesurface = win.createSurfaceLocked();

if(surface != null) {

//先创建一个本地surface,然后在outSurface的对象上调用copyFrom

//将本地Surface的信息复制到outSurface中,为什么要这么麻烦呢?

outSurface.copyFrom(surface);

......

}

[-->WindowManagerService.java::WindowState]

Surface createSurfaceLocked() {

......

try {

//mSurfaceSession就是在Session上创建的SurfaceSession对象

//这里,以它为參数。构造一个新的Surface对象

mSurface = new Surface(

mSession.mSurfaceSession, mSession.mPid,

mAttrs.getTitle().toString(),

0, w, h, mAttrs.format, flags);

}

Surface.openTransaction();//打开一个事务处理

......

Surface.closeTransaction();//关闭一个事务处理。关于事务处理以后再分析

......

}

上面的代码段好像有点混乱。用图8-7来表示一下这个流程:

图8-7  复杂的Surface创建流程

依据图8-7可知:

·  WMS中的Surface是乾坤中的乾。它的构造使用了带SurfaceSession參数的构造函数。

·  ViewRoot中的Surface是乾坤中的坤。它的构造使用了无參构造函数。

·  copyFrom就是挪移。它将乾中的Surface信息。复制到坤中的Surface即outSurface里。

要是觉得乾坤大挪移就是这两三下。未免就太小看它了。为彻底揭示这期间的复杂过程,我们将使用必杀技——aidl工具。

2. 揭秘Surface的乾坤大挪移

aidl能够把XXX.aidl文件转换成相应的Java文件。刚才所说的乾坤大挪移发生在ViewRoot调用IWindowSession的relayout函数中,它在IWindowSession.adil中的定义例如以下:

[-->IWindowSesson.aidl]

interface IWindowSession {

......

intrelayout(IWindow window, in WindowManager.LayoutParams attrs,

int requestedWidth, int requestedHeight, int viewVisibility,

boolean insetsPending, out Rect outFrame, out Rect outContentInsets,

out Rect outVisibleInsets, out Configuration outConfig,

out Surface outSurface);

以下,拿必杀技aidl来编译一下这个aidl文件,其使用方法例如以下:

在命令行下能够输入:

aidl –Ie:\froyo\source\frameworks\base\core\java\ -Ie:\froyo\source\frameworks\base\Graphics\java e:\froyo\source\frameworks\base\core\java\android\view\IWindowSession.aidltest.java

新生成的Java文件叫test.java。当中,-I參数指定include文件夹。比如aidl文件里使用了别的Java文件里的类。所以须要指定这些Java文件所在的文件夹。

先看ViewRoot这个客户端生成的代码。例如以下所看到的:

[-->test.java::Bp端::relayout]

public int relayout(android.view.IWindow window,

android.view.WindowManager.LayoutParams attrs,

int requestedWidth, intrequestedHeight,

int viewVisibility, boolean insetsPending,

android.graphics.Rect outFrame,

android.graphics.Rect outContentInsets,

android.graphics.Rect outVisibleInsets,

android.content.res.Configuration outConfig,

android.view.Surface outSurface)//outSurface是第11个參数

throwsandroid.os.RemoteException

{

android.os.Parcel_data = android.os.Parcel.obtain();

android.os.Parcel_reply = android.os.Parcel.obtain();

int_result;

try {

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder((((window!=null))?(window.asBinder()):(null)));

if((attrs!=null)) {

_data.writeInt(1);

attrs.writeToParcel(_data,0);

}

else {

_data.writeInt(0);

}

_data.writeInt(requestedWidth);

_data.writeInt(requestedHeight);

_data.writeInt(viewVisibility);

_data.writeInt(((insetsPending)?(1):(0)));

//奇怪,outSurface的信息没有写到请求包_data中,就直接发送请求消息了

mRemote.transact(Stub.TRANSACTION_relayout,_data, _reply, 0);

_reply.readException();

_result= _reply.readInt();

if((0!=_reply.readInt())) {

outFrame.readFromParcel(_reply);

}

....

if((0!=_reply.readInt())) {

outSurface.readFromParcel(_reply);//从Parcel中读取信息来填充outSurface

}

}

......

return_result;

}

奇怪!

ViewRoot调用requestlayout居然没有把outSurface信息传进去,这么说。服务端收到的Surface对象应该就是空吧?那怎么能调用copyFrom呢?还是来看服务端的处理。先看首先收到消息的onTransact函数。代码例如以下所看到的:

[-->test.java::Bn端::onTransact]

public boolean onTransact(int code,android.os.Parcel data,

android.os.Parcelreply, int flags)

throwsandroid.os.RemoteException

{

switch(code)

{

caseTRANSACTION_relayout:

{

data.enforceInterface(DESCRIPTOR);

android.view.IWindow_arg0;

android.view.Surface_arg10;

//刚才讲了,Surface信息并没有传过来。那么在relayOut中看到的outSurface是怎么

//出来的呢?看以下这句可知。原来在服务端这边居然new了一个新的Surface!!!

_arg10= new android.view.Surface();

int_result = this.relayout(_arg0, _arg1, _arg2, _arg3, _arg4,

_arg5,_arg6, _arg7, _arg8, _arg9, _arg10);

reply.writeNoException();

reply.writeInt(_result);

//_arg10就是调用copyFrom的那个outSurface,那怎么传到客户端呢?

if((_arg10!=null)) {

reply.writeInt(1);

//调用Surface的writeToParcel,把信息写到reply包中。

//注意最后一个參数为PARCELABLE_WRITE_RETURN_VALUE

_arg10.writeToParcel(reply,

android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

}

}

......

returntrue;

}

看完这个。会让人有点毛骨悚然。我最開始一直在JNI文件里寻找大挪移的踪迹,但有几个关键点始终不能明确,万不得已就使用了这个aidl必杀技,于是终于揭露出其真相了。

3. 乾坤大挪移的真相

这里,总结一下乾坤大挪移的整个过程,如图8-8表示:

图8-8  乾坤大挪移的真面目

上图非常清楚地列出了乾坤大挪移的过程。我们可结合代码来加深理解。

注意。这里,将BpWindowSession作为了IWindowSessionBinder在客户端的代表。

8.3.3分析乾坤大挪移的JNI层

前文讲述的内容都集中在Java层,以下要依照流程顺序分析JNI层的内容。

1. Surface的无參构造分析

在JNI层,第一个被调用的是Surface的无參构造函数,其代码例如以下所看到的:

[-->Surface.java]

public Surface() {

......

//CompatibleCanvas从Canvas类派生

mCanvas = new CompatibleCanvas();

}

Canvas是什么?依据SDK文档的介绍可知,画图须要“四大金刚”相互合作。这四大金刚是:

·  Bitmap:用于存储像素,也就是画布。

可把它当做一块数据存储区域。

·  Canvas:用于记载画图的动作,比方画一个圆,画一个矩形等。Canvas类提供了这些主要的画图函数。

·  Drawing primitive:画图基元。比如矩形、圆、弧线、文本、图片等。

·  Paint:它用来描写叙述绘画时使用的颜色、风格(如实线、虚线等)等。

在普通情况下。Canvas会封装一块Bitmap。而作图就是基于这块Bitmap的。前面说的画布,事实上指的就是Canvas中的这块Bitmap。

这些知识稍了解就可以,不必去深究。Surface的无參构造函数没有什么有价值的内容,接着看以下的内容。

2. SurfaceSession的构造

如今要分析的是SurfaceSession,其构造函数例如以下所看到的:

[-->SurfaceSession.java]

public SurfaceSession() {

init();//这是一个native函数

}

init是一个native函数。去看看它的JNI实现,它在android_view_Surface.cpp中,代码例如以下所看到的:

[-->android_view_Surface.cpp]

static void SurfaceSession_init(JNIEnv* env,jobject clazz)

{

//创建一个SurfaceComposerClient对象

sp<SurfaceComposerClient> client = new SurfaceComposerClient;

client->incStrong(clazz);

//在Java对象中保存这个client对象的指针,类型为SurfaceComposerClient

env->SetIntField(clazz, sso.client, (int)client.get());

}

这里先不讨论SurfaceComposerClient的内容,拟继续把乾坤大挪移的流程走完。

3. Surface的有參构造

下一个调用的是Surface的有參构造,其參数中有一个SurfaceSession。

先看Java层的代码,例如以下所看到的:

[-->Surface.java]

publicSurface(SurfaceSession s,//传入一个SurfaceSession对象

int pid, String name, int display, int w, int h, int format, int flags)

throws OutOfResourcesException {

......

mCanvas = new CompatibleCanvas();

//又一个native函数,注意传递的參数:display以后再说,w,h代表画图区域的宽高值

init(s,pid,name,display,w,h,format,flags);

mName = name;

}

Surface的native init函数的JNI实现。也在android_view_Surface.cpp中,一起来看:

[-->android_view_Surface.cpp]

static void Surface_init(

JNIEnv*env, jobject clazz,

jobject session,

jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jintflags)

{

//从SurfaceSession对象中取出之前创建的那个SurfaceComposerClient对象

SurfaceComposerClient* client =

(SurfaceComposerClient*)env->GetIntField(session, sso.client);

sp<SurfaceControl> surface;//注意它的类型是SurfaceControl

if (jname == NULL) {

/*

调用SurfaceComposerClient的createSurface函数,返回的surface是一个

SurfaceControl类型。

*/

surface = client->createSurface(pid, dpy, w, h, format, flags);

} else{

......

}

//把这个surfaceControl对象设置到Java层的Surface对象中,对这个函数就不再分析了

setSurfaceControl(env, clazz, surface);

}

4. copyFrom的分析

如今要分析的就是copyFrom了。

它就是一个native函数。看它的JNI层代码:

[-->android_view_Surface.cpp]

static void Surface_copyFrom(JNIEnv* env,jobject clazz, jobject other)

{

//依据JNI函数的规则,clazz是copyFrom的调用对象,而other是copyFrom的參数。

//目标对象此时还没有设置SurfaceControl,而源对象在前面已经创建了SurfaceControl

constsp<SurfaceControl>& surface = getSurfaceControl(env, clazz);

constsp<SurfaceControl>& rhs = getSurfaceControl(env, other);

if (!SurfaceControl::isSameSurface(surface, rhs)) {

//把源SurfaceControl对象设置到目标Surface中。

setSurfaceControl(env, clazz, rhs);

}

}

这一步还是比較简单的,以下看第五步writeToParcel函数的调用。

5. writeToParcel的分析

多亏了必杀技aidl工具的帮忙,才挖出这个隐藏的writeToParcel函数调用。以下就来看看它,代码例如以下所看到的:

[-->android_view_Surface.cpp]

static void Surface_writeToParcel(JNIEnv* env,jobject clazz,

jobject argParcel, jint flags)

{

Parcel* parcel = (Parcel*)env->GetIntField(argParcel, no.native_parcel);

//clazz就是Surface对象,从这个Surface对象中取出保存的SurfaceControl对象

const sp<SurfaceControl>&control(getSurfaceControl(env, clazz));

/*

把SurfaceControl中的信息写到Parcel包中。然后利用Binder通信传递到对端。

对端通过readFromParcel来处理Parcel包。

*/

SurfaceControl::writeSurfaceToParcel(control, parcel);

if (flags & PARCELABLE_WRITE_RETURN_VALUE) {

//还记得PARCELABLE_WRITE_RETURN_VALUE吗?flags的值就等于它

//所以本地Surface对象的SurfaceControl值被置空了

setSurfaceControl(env, clazz, 0);

}

}

6. readFromParcel的分析

再看作为客户端的ViewRoot所调用的readFromParcel函数。

它也是一个native函数,JNI层的代码例如以下所看到的:

[-->android_view_Surface.cpp]

static void Surface_readFromParcel(

JNIEnv* env, jobject clazz, jobject argParcel)

{

Parcel* parcel = (Parcel*)env->GetIntField( argParcel,no.native_parcel);

//注意以下定义的变量类型是Surface,而不是SurfaceControl

const sp<Surface>&control(getSurface(env, clazz));

//依据服务端传递的Parcel包来构造一个新的surface。

sp<Surface> rhs = new Surface(*parcel);

if (!Surface::isSameSurface(control, rhs)) {

//把这个新surface赋给ViewRoot中的mSurface对象。

setSurface(env,clazz, rhs);

}

}

7. Surface乾坤大挪移的小结

可能有人会问,乾坤大挪移怎么这么复杂?这期间出现了多少对象?来总结一下。在此期间一共同拥有三个关键对象(注意我们这里仅仅考虑JNI层的Native对象),它们各自是:

·  SurfaceComposerClient。

·  SurfaceControl。

·  Surface,这个Surface对象属于Native层。和Java层的Surface相相应。

当中转移到ViewRoot成员变量mSurface中的,就是最后这个Surface对象了。这一路走来。真是异常坎坷。来回想并概括总结一下这段历程。至于它的作用应该是非常清楚了。以后要破解SurfaceFlinger。靠的就是这个精简的流程。

·  创建一个SurfaceComposerClient。

·  调用SurfaceComposerClient的createSurface得到一个SurfaceControl对象。

·  调用SurfaceControl的writeToParcel把一些信息写到Parcel包中。

·  依据Parcel包的信息构造一个Surface对象。这个Surface对象保存到Java层的mSurface对象中。这样,大挪移的结果是ViewRoot得到一个Native的Surface对象。

精简流程后。寥寥数语就可把过程说清楚。以后我们在研究代码时。也能够採取这样的方式。

这个Surface对象非常重要,可它究竟有什么用呢?这正是下一节要讲的内容。

8.3.4  Surface和画图

以下,来看最后两个和Surface相关的函数调用:一个是lockCanvas;另外一个是unlockCanvasAndPost。

1. lockCanvas的分析

要对lockCanvas进行分析,须先来看Java层的函数,代码例如以下所看到的:

[-->Surface.java::lockCanvas()]

public Canvas lockCanvas(Rect dirty)

throws OutOfResourcesException,IllegalArgumentException

{

return lockCanvasNative(dirty);//调用native的lockCanvasNative函数。

}

[-->android_view_Surface.cpp::Surface_lockCanvas()]

static jobject Surface_lockCanvas(JNIEnv* env,jobject clazz, jobject dirtyRect)

{

//从Java中的Surface对象中,取出费尽千辛万苦得到的Native的Surface对象

constsp<Surface>& surface(getSurface(env, clazz));

......

// dirtyRect表示须要重绘的矩形块,以下依据这个dirtyRect设置dirtyRegion

RegiondirtyRegion;

if(dirtyRect) {

Rect dirty;

dirty.left  =env->GetIntField(dirtyRect, ro.l);

dirty.top   =env->GetIntField(dirtyRect, ro.t);

dirty.right = env->GetIntField(dirtyRect, ro.r);

dirty.bottom=env->GetIntField(dirtyRect, ro.b);

if(!dirty.isEmpty()) {

dirtyRegion.set(dirty);

}

} else{

dirtyRegion.set(Rect(0x3FFF,0x3FFF));

}

//调用NativeSurface对象的lock函数。

//传入了一个參数Surface::SurfaceInfo info和一块表示脏区域的dirtyRegion

Surface::SurfaceInfo info;

status_t err = surface->lock(&info, &dirtyRegion);

......

//Java的Surface对象构造的时候会创建一个CompatibleCanvas。

//这里就取出这个CompatibleCanvas对象

jobject canvas = env->GetObjectField(clazz, so.canvas);

env->SetIntField(canvas, co.surfaceFormat, info.format);

//从Canvas对象中取出SkCanvas对象

SkCanvas* nativeCanvas =(SkCanvas*)env->GetIntField(

canvas, no.native_canvas);

SkBitmap bitmap;

ssize_t bpr = info.s *bytesPerPixel(info.format);

bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);

......

if (info.w > 0 && info.h > 0) {

//info.bits指向一块存储区域。

bitmap.setPixels(info.bits);

} else{

bitmap.setPixels(NULL);

}

//给这个SkCanvas设置一个Bitmap。还记得前面说的,画图须要的四大金刚吗?

//这里将Bitmap设置到这个Canvas中,这样进UI绘画时就有画布了。

nativeCanvas->setBitmapDevice(bitmap);

......

returncanvas;

}

lockCanvas还算比較简单:

·  先获得一块存储区域,然后将它和Canvas绑定到一起。这样。UI绘画的结果就记录在这块存储区域里了。

注意。本书不拟讨论Android系统上Skia和OpenGL方面的知识,有兴趣的读者可自行研究。

接下来看unlockCanvasAndPost函数,它也是一个native函数:

2. unlockCanvasAndPost的分析

来看unlockCanvasAndPost的代码,例如以下所看到的:

[-->android_view_Surface.cpp]

static void Surface_unlockCanvasAndPost(JNIEnv*env, jobject clazz,

jobject argCanvas)

{

jobjectcanvas = env->GetObjectField(clazz, so.canvas);

//取出Native的Surface对象

const sp<Surface>& surface(getSurface(env,clazz));

//以下这些内容。不拟讨论。读者若有兴趣。可结合Skia库,自行研究。

SkCanvas* nativeCanvas =(SkCanvas*)env->GetIntField(canvas,

no.native_canvas);

intsaveCount = env->GetIntField(clazz, so.saveCount);

nativeCanvas->restoreToCount(saveCount);

nativeCanvas->setBitmapDevice(SkBitmap());

env->SetIntField(clazz, so.saveCount, 0);

//调用Surface对象的unlockAndPost函数。

status_t err = surface->unlockAndPost();

......

}

unlockCanvasAndPost也非常easy,这里就不再多说了。

8.3.5初识Surface总结

在本节的最后,我们来概括总结一下这一节所涉及到和Surface相关的调用流程。以备攻克下一个难关,如图8-9所看到的 :

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-9  Surface的精简流程图

8.4深入分析Surface

这一节,拟基于图8-9中的流程。对Surface进行深入分析。在分析之前。还须要介绍一些Android平台上图形/图像显示方面的知识,这里统称之为与Surface相关的基础知识。

8.4.1与Surface相关的基础知识介绍

1. 显示层(Layer)和屏幕组成

你了解屏幕显示的美丽界面是如何组织的吗?来看图8-10所展示的屏幕组成示意图:

图8-10  屏幕组成示意图

从图8-10中能够看出:

·  屏幕位于一个三维坐标系中,当中Z轴从屏幕内指向屏幕外。

·  编号为①②③的矩形块叫显示层(Layer)。每一层有自己的属性,比如颜色、透明度、所处屏幕的位置、宽、高等。

除了属性之外,每一层还有自己相应的显示内容,也就是须要显示的图像。

在Android中。Surface系统工作时。会由SurfaceFlinger对这些依照Z轴排好序的显示层进行图像混合,混合后的图像就是在屏幕上看到的美妙画面了。这样的按Z轴排序的方式符合我们在日常生活中的体验。比如前面的物体会遮挡住后面的物体。

注意,Surface系统中定义了一个名为Layer类型的类,为了区分广义概念上的Layer和代码中的Layer,这里称广义层的Layer为显示层。以免混淆。

Surface系统提供了三种属性,一共四种不同的显示层。

简介一下:

·  第一种属性是eFXSurfaceNormal属性。大多数的UI界面使用的就是这样的属性。它有两种模式:

1)Normal模式,这样的模式的数据。是通过前面的mView.draw(canvas)画上去的。

这也是绝大多数UI所採用的方式。

2)PushBuffer模式,这样的模式相应于视频播放、摄像机摄录/预览等应用场景。以摄像机为例,当摄像机运行时,来自Camera的预览数据直接push到Buffer中,无须应用层自己再去draw了。

·  另外一种属性是eFXSurfaceBlur属性。这样的属性的UI有点朦胧美。看起来非常像隔着一层毛玻璃。

·  第三种属性是eFXSurfaceDim属性,这样的属性的UI看起来有点暗,好像隔了一层深色玻璃。从视觉上讲,尽管它的UI看起来有点暗,但并不模糊。而eFXSurfaceBlur不仅暗,还有些模糊。

图8-11展示了最后两种类型的视觉效果图,当中左边的是Blur模式,右边的是Dim模式。

图8-11  Blur和Dim效果图

注意,关于Surface系统的显示层属性定义,读者可參考ISurfaceComposer.h。

本章将重点分析第一种属性的两类显示层的工作原理。

2. FrameBuffer和PageFlipping

我们知道,在Audio系统中,音频传输数据的过程是:

·  由客户端把数据写到共享内存中。

·  然后由AudioFlinger从共享内存中取出数据再往Audio HAL中发送。

依据以上介绍可知,在音频传输数据的过程中,共享内存起到了数据承载的重要作用。                                                                                     无独有偶,Surface系统中的传输数据也存在相同的过程,但承载图像数据的是鼎鼎大名的FrameBuffer(简称FB)。以下先来介绍FrameBuffer。然后再介绍Surface的传输数据过程。

(1)FrameBuffer的介绍

FrameBuffer的中文名叫帧缓冲,它实际上包括两个不同的方面:

·  Frame:帧,就是指一幅图像。在屏幕上看到的那幅图像就是一帧。

·  Buffer:缓冲,就是一段存储区域。可这个区域存储的是帧。

FrameBuffer的概念非常清楚,它就是一个存储图形/图像帧数据的缓冲。这个缓冲来自哪里?理解这个问题,须要简介一下Linux平台的虚拟显示设备FrameBuffer Device(简称FBD)。FBD是Linux系统中的一个虚拟设备,设备文件相应为/dev/fb%d(比方/dev/fb0)。这个虚拟设备将不同硬件厂商实现的真实设备统一在一个框架下,这样应用层就能够通过标准的接口进行图形/图像的输入和输出了。图8-12展示了FBD示意图:

图8-12  Linux系统中的FBD示意图

从上图中能够看出,应用层通过标准的ioctl或mmap等系统调用,就能够操作显示设备。用起来非常方便。

这里,把mmap的调用列出来,相信大部分读者都知道它的作用了。

FrameBuffer中的Buffer,就是通过mmap把设备中的显存映射到用户空间的,在这块缓冲上写数据,就相当于在屏幕上绘画。

注意:上面所说的框架将引出另外一个概念Linux FrameBuffer(简称LFB)。LFB是Linux平台提供的一种可直接操作FB的机制,依托这个机制,应用层通过标准的系统调用。就能够操作显示设备了。从使用的角度来看,它和Linux Audio中的OSS有些相似。

为加深读者对此节内容的理解。这里给出一个小样例。就是在DDMS工具中实现屏幕截图功能,其代码在framebuffer_service.c中。例如以下所看到的:

[-->framebuffer_service.c]

struct fbinfo {//定义一个结构体

unsigned int version;

unsigned int bpp;

unsigned int size;

unsigned int width;

unsigned int height;

unsigned int red_offset;

unsigned int red_length;

unsigned int blue_offset;

unsigned int blue_length;

unsigned int green_offset;

unsigned int green_length;

unsigned int alpha_offset;

unsigned int alpha_length;

} __attribute__((packed));

//fd是一个文件的描写叙述符,这个函数的目的,是把当前屏幕的内容写到一个文件里

void framebuffer_service(int fd, void *cookie)

{

structfb_var_screeninfo vinfo;

intfb, offset;

charx[256];

structfbinfo fbinfo;

unsigned i, bytespp;

//Android系统上的fb设备路径在/dev/graphics文件夹下

fb =open("/dev/graphics/fb0", O_RDONLY);

if(fb< 0) goto done;

//取出屏幕的属性

if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) goto done;

fcntl(fb, F_SETFD, FD_CLOEXEC);

bytespp = vinfo.bits_per_pixel / 8;

//依据屏幕的属性填充fbinfo结构。这个结构要写到输出文件的头部

fbinfo.version = DDMS_RAWIMAGE_VERSION;

fbinfo.bpp = vinfo.bits_per_pixel;

fbinfo.size = vinfo.xres * vinfo.yres * bytespp;

fbinfo.width = vinfo.xres;

fbinfo.height = vinfo.yres;

/*

以下几个变量和颜色格式有关,以RGB565为例,简介一下。

RGB565表示一个像素点中R分量为5位。G分量为6位,B分量为5位,而且没有Alpha分量。

这样一个像素点的大小为16位,占两个字节,比RGB888格式的一个像素少一个字节(它一个像素是三个字节)。

x_length的值为x分量的位数。比如,RGB565中R分量就是5位。

x_offset的值代表x分量在内存中的位置。如RGB565一个像素占两个字节。那么x_offeset

表示x分量在这两个字节内存区域中的起始位置,但这个顺序是反的。也就是B分量在前,

R在最后。

所以red_offset的值就是11。而blue_offset的值是0,green_offset的值是6。

这些信息在做格式转换时(比如从RGB565转到RGB888的时候)实用。

*/

fbinfo.red_offset = vinfo.red.offset;

fbinfo.red_length = vinfo.red.length;

fbinfo.green_offset = vinfo.green.offset;

fbinfo.green_length = vinfo.green.length;

fbinfo.blue_offset = vinfo.blue.offset;

fbinfo.blue_length = vinfo.blue.length;

fbinfo.alpha_offset = vinfo.transp.offset;

fbinfo.alpha_length = vinfo.transp.length;

offset= vinfo.xoffset * bytespp;

offset+= vinfo.xres * vinfo.yoffset * bytespp;

//将fb信息写到文件头部

if(writex(fd, &fbinfo, sizeof(fbinfo))) goto done;

lseek(fb, offset, SEEK_SET);

for(i= 0; i < fbinfo.size; i += 256) {

if(readx(fb, &x, 256)) goto done;//读取FBD中的数据

if(writex(fd, &x, 256)) goto done;//将数据写到文件

}

if(readx(fb, &x, fbinfo.size % 256)) goto done;

if(writex(fd, &x, fbinfo.size % 256)) goto done;

done:

if(fb>= 0) close(fb);

close(fd);

}

上面函数的目的就是截屏,这个样例可加深我们对FB的直观感受,相信读者下次再碰到FB时就不会犯怵了。

注意:我们可依据这段代码,写一个简单的Native可运行程序,然后adb push到设备上运行。

注意上面写到文件里的是RGB565格式的原始数据。如想在台式机上看到这幅图片。可将它转换成BMP格式。我的个人博客上提供一个RGB565转BMP的程序。读者能够下载或自己另写一个,这样也许有助于更深入理解图形/图像方面的知识。

在继续分析前,先来问一个问题:

前面在Audio系统中讲过,CB对象通过读写指针来协调生产者/消费者的步调,那么Surface系统中的传输数据过程,是否也需通过读写指针来控制呢?

答案是肯定的,但不像Audio中的CB那样复杂。

(2)PageFlipping

图形/图像数据和音频数据不太一样,我们一般把音频数据叫音频流,它是没有边界的, 而图形/图像数据是一帧一帧的,是有边界的。

这一点非常相似UDP和TCP之间的差别。所以在图形/图像数据的生产/消费过程中,人们使用了一种叫PageFlipping的技术。

PageFlipping的中文名叫画面交换,其操作步骤例如以下所看到的:

·  分配一个能容纳两帧数据的缓冲,前面一个缓冲叫FrontBuffer。后面一个缓冲叫BackBuffer。

·  消费者使用FrontBuffer中的旧数据。而生产者用新数据填充BackBuffer,二者互不干扰。

·  当须要更新显示时,BackBuffer变成FrontBuffer。FrontBuffer变成BackBuffer。如此循环。这样就总能显示最新的内容了。这个过程非常像我们寻常的翻书动作。所以它被形象地称为PageFlipping。

说白了,PageFlipping事实上就是使用了一个仅仅有两个成员的帧缓冲队列,以后在分析传输数据的时候还会见到诸如dequeue和queue的操作。

3. 图像混合

我们知道,在AudioFlinger中有混音线程,它能将来自多个数据源的数据混合后输出,那么。SurfaceFlinger是不是也具有相同的功能呢?

答案是肯定的,否则它就不会叫Flinger了。Surface系统支持软硬两个层面的图像混合:

·  软件层面的混合:比如使用copyBlt进行源数据和目标数据的混合。

·  硬件层面的混合:使用Overlay系统提供的接口。

无论是硬件还是软件层面,都需将源数据和目标数据进行混合。混合需考虑非常多内容。比如源的颜色和目标的颜色叠加后所产生的颜色。关于这方面的知识,读者能够学习计算机图形/图像学。这里仅仅简介一下copyBlt和Overlay。

·  copyBlt,从名字上看。是数据拷贝,它也能够由硬件实现。比如如今非常多的2D图形加速就是将copyBlt改由硬件来实现,以提快速度的。但不必关心这些,我们仅仅需关心如何调用copyBlt相关的函数进行数据混合就可以。

·  Overlay方法必须有硬件支持才干够,它主要用于视频的输出,比如视频播放、摄像机摄像等,由于视频的内容往往变化非常快,所以如改用硬件进行混合效率会更高。

总体来说,Surface是一个比較庞大的系统,由于篇幅和精力所限。本章后面的内容将重点关注Surface系统的框架和工作流程。在掌握框架和流程后。读者就能够在大的脉络中迅速定位到自己感兴趣的地方,然后展开更深入的研究了。

以下通过图8-9所看到的的精简流程,深入分析Android的Surface系统。

8.4.2  SurfaceComposerClient的分析

SurfaceComposerClient的出现是由于:

Java层SurfaceSession对象的构造函数会调用Native的SurfaceSession_init函数,而该函数的主要目的就是创建SurfaceComposerClient。

先回想一下SurfaceSession_init函数,代码例如以下所看到的:

[-->android_view_Surface.cpp]

static void SurfaceSession_init(JNIEnv* env,jobject clazz)

{

//new 一个SurfaceComposerClient对象

sp<SurfaceComposerClient> client = newSurfaceComposerClient;

//sp的使用也有让人烦恼的地方。有时须要显式地增加强弱引用计数,要是忘记,可就麻烦了

client->incStrong(clazz);

env->SetIntField(clazz, sso.client,(int)client.get());

}

上面代码中。显式地构造了一个SurfaceComposerClient对象。接下来看它是何方神圣。

1. 创建SurfaceComposerClient

SurfaceComposerClient这个名字隐含的意思是:

这个对象会和SurfaceFlinger进行交互,由于SurfaceFlinger派生于SurfaceComposer。

通过它的构造函数来看是否是这样的。代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

SurfaceComposerClient::SurfaceComposerClient()

{

//getComposerService()将返回SF的Binder代理端的BpSurfaceFlinger对象

sp<ISurfaceComposer> sm(getComposerService());

//先调用SF的createConnection。再调用_init

_init(sm, sm->createConnection());

if(mClient != 0) {

Mutex::Autolock _l(gLock);

//gActiveConnections是全局变量,把刚才创建的client保存到这个map中去

gActiveConnections.add(mClient->asBinder(), this);

}

}

果然如此,SurfaceComposerClient建立了和SF的交互通道,以下直接转到SF的createConnection函数去观察。

(1)createConnection的分析

直接看代码,例如以下所看到的:

[-->SurfaceFlinger.cpp]

sp<ISurfaceFlingerClient>SurfaceFlinger::createConnection()

{

Mutex::Autolock _l(mStateLock);

uint32_t token = mTokens.acquire();

//先创建一个Client。

sp<Client> client = new Client(token, this);

//把这个Client对象保存到mClientsMap中,token是它的标识。

status_t err = mClientsMap.add(token, client);

/*

创建一个用于Binder通信的BClient,BClient派生于ISurfaceFlingerClient,

它的作用是接受客户端的请求,然后把处理提交给SF。注意,并非提交给Client。

Client会创建一块共享内存,该内存由getControlBlockMemory函数返回

*/

sp<BClient> bclient =

new BClient(this, token,client->getControlBlockMemory());

returnbclient;

}

上面代码中提到,Client会创建一块共享内存。

熟悉Audio的读者也许会觉得,这可能是Surface的ControlBlock对象了!

是的。CB对象在协调生产/消费步调时,起到了决定性的控制作用。所以非常重要,以下来看:

[-->SurfaceFlinger.cpp]

Client::Client(ClientID clientID, constsp<SurfaceFlinger>& flinger)

:ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)

{

const int pgsize = getpagesize();

//以下这个操作会使cblksize为页的大小,眼下是4096字节。

constint cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));

//MemoryHeapBase是我们的老朋友了。不熟悉的读者能够回想Audio系统中所介绍的内容

mCblkHeap = new MemoryHeapBase(cblksize, 0,

"SurfaceFlinger Clientcontrol-block");

ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());

if(ctrlblk) {

new(ctrlblk) SharedClient; //再一次觉得眼熟吧?使用了placement new

}

}

原来,Surface的CB对象就是在共享内存中创建的这个SharedClient对象。

先来认识一下这个SharedClient。

(2)SharedClient的分析

SharedClient定义了一些成员变量,代码例如以下所看到的:

class SharedClient

{

public:

SharedClient();

~SharedClient();

status_t validate(size_t token) const;

uint32_t getIdentity(size_t token) const;//取出标识本Client的token

private:

Mutexlock;

Condition cv; //支持跨进程的同步对象

//NUM_LAYERS_MAX为31,SharedBufferStack是什么?

SharedBufferStack surfaces[ NUM_LAYERS_MAX ];

};

//SharedClient的构造函数,没什么新意。不如Audio的CB对象复杂

SharedClient::SharedClient()

:lock(Mutex::SHARED), cv(Condition::SHARED)

{

}

SharedClient的定义似乎简单到极致了,只是不要高兴得过早,在这个SharedClient的定义中,没有发现和读写控制相关的变量,那怎么控制读写呢?

答案就在看起来非常别扭的SharedBufferStack数组中,它有31个元素。关于它的作用就不必卖关子了,答案是:

一个Client最多支持31个显示层。

每一个显示层的生产/消费步调都由会相应的SharedBufferStack来控制。而它内部就用了几个成员变量来控制读写位置。

认识一下SharedBufferStack的这几个控制变量。例如以下所看到的:

[-->SharedBufferStack.h]

class  SharedBufferStack{

......

//Buffer是按块使用的,每一个Buffer都有自己的编号,事实上就是数组中的索引號。

volatile int32_t head;     //FrontBuffer的编号

volatile int32_t available; //空暇Buffer的个数

volatile int32_t queued;  //脏Buffer的个数。脏Buffer表示有新数据的Buffer

volatile int32_t inUse; //SF当前正在使用的Buffer的编号

volatilestatus_t status; //状态码

......

}

注意,上面定义的SharedBufferStack是一个通用的控制结构。而不仅是针对于仅仅有两个Buffer的情况。

依据前面介绍的PageFlipping知识,假设仅仅有两个FB,那么,SharedBufferStack的控制就比較简单了:

要么SF读1号Buffer。客户端写0号Buffer。要么SF读0号Buffer,客户端写1号Buffer。

图8-13是展示了SharedClient的示意图:

图8-13  SharedClient的示意图

从上图可知:

·  SF的一个Client分配一个跨进程共享的SharedClient对象。这个对象有31个SharedBufferStack元素。每一个SharedBufferStack相应于一个显示层。

·  一个显示层将创建两个Buffer,兴许的PageFlipping就是基于这两个Buffer展开的。

另外。每一个显示层中。其数据的生产和消费并非直接使用SharedClient对象来进行具体控制的。而是基于SharedBufferServer和SharedBufferClient两个结构,由这两个结构来对该显示层使用的SharedBufferStack进行操作,这些内容在以后的分析中还会碰到。

注意。这里的显示层指的是Normal类型的显示层。

来接着分析后面的_init函数。

(3)_init函数的分析

先回想一下之前的调用,代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

SurfaceComposerClient::SurfaceComposerClient()

{

......

_init(sm, sm->createConnection());

......

}

来看这个_init函数。代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

void SurfaceComposerClient::_init(

const sp<ISurfaceComposer>& sm, constsp<ISurfaceFlingerClient>& conn)

{

mPrebuiltLayerState = 0;

mTransactionOpen = 0;

mStatus = NO_ERROR;

mControl = 0;

mClient = conn;//mClient就是BClient的客户端

mControlMemory =mClient->getControlBlock();

mSignalServer = sm;// mSignalServer就是BpSurfaceFlinger

//mControl就是那个创建于共享内存之中的SharedClient

mControl = static_cast<SharedClient*>(mControlMemory->getBase());

}

_init函数的作用,就是初始化SurfaceComposerClient中的一些成员变量。最重要的是得到了三个成员:

·  mSignalServer ,它事实上是SurfaceFlinger在客户端的代理BpSurfaceFlinger,它的主要作用是,在客户端更新完BackBuffer后(也就是刷新了界面后),通知SF进行PageFlipping和输出等工作。

·  mControl。它是跨进程共享的SharedClient,是Surface系统的ControlBlock对象。

·  mClient。它是BClient在客户端的相应物。

2. 究竟有多少种对象?

这一节,出现了好几种类型的对象,通过图8-14来看看它们:

图8-14  类之间关系展示图

从上图中能够看出:

·  SurfaceFlinger是从Thread派生的。所以它会有一个单独运行的工作线程。

·  BClient和SF之间採用了Proxy模式,BClient支持Binder通信。它接收客户端的请求,并派发给SF运行。

·  SharedClient构建于一块共享内存中,SurfaceComposerClient和Client对象均持有这块共享内存。

在精简流程中,关于SurfaceComposerClient就分析到这里。以下分析第二个步骤中的SurfaceControl对象。

8.4.3  SurfaceControl的分析

1. SurfaceControl的来历

依据精简的流程可知。这一节要分析的是SurfaceControl对象。先回想一下这个对象的创建过程。代码例如以下所看到的:

[-->android_view_Surface.cpp]

static void Surface_init(JNIEnv* env, jobjectclazz, jobject session,

jint pid, jstring jname, jint dpy, jint w, jint h, jint format, jintflags)

{

SurfaceComposerClient* client =

(SurfaceComposerClient*)env->GetIntField(session, sso.client);

//注意这个变量。类型是SurfaceControl。名字却叫surface。稍不留神就出错了。

sp<SurfaceControl>surface;

if (jname == NULL) {

//调用Client的createSurface函数。得到一个SurfaceControl对象。

surface= client->createSurface(pid, dpy, w, h, format, flags);

}

......

//将这个SurfaceControl对象设置到Java层的对象中保存。

setSurfaceControl(env, clazz, surface);

}

通过上面的代码可知,SurfaceControl对象由createSurface得来,以下看看这个函数。

此时,读者也许会被代码中任意起的变量名搞糊涂。由于我的处理方法碰到了easy混淆的地方,尽量以对象类型来表示这个对象。

(1)分析createSurface的请求端

在createSurface内部会使用Binder通信将请求发给SF,所以它分为请求和响应两端,先看请求端,代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

sp<SurfaceControl>SurfaceComposerClient::createSurface(

int pid,

DisplayID display,//DisplayID是什么意思?

uint32_t w,

uint32_t h,

PixelFormat format,

uint32_t flags)

{

String8 name;

constsize_t SIZE = 128;

charbuffer[SIZE];

snprintf(buffer, SIZE, "<pid_%d>", getpid());

name.append(buffer);

//调用另外一个createSurface。多一个name參数

returnSurfaceComposerClient::createSurface(pid, name, display,

w, h, format, flags);

}

在分析另外一个createSurface之前。应先介绍一下DisplayID的含义:

typedef int32_t    DisplayID;

DisplayID是一个int整型,它的意义是屏幕编号,比如双屏手机就有内屏和外屏两块屏幕。由于眼下Android的Surface系统仅仅支持一块屏幕,所以这个变量的取值都是0。

再分析另外一个createSurface函数。它的代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

sp<SurfaceControl>SurfaceComposerClient::createSurface(

int pid,const String8& name,DisplayID display,uint32_t w,

uint32_t h,PixelFormat format,uint32_t flags)

{

sp<SurfaceControl> result;

if(mStatus == NO_ERROR) {

ISurfaceFlingerClient::surface_data_t data;

//调用BpSurfaceFlingerClient的createSurface函数

sp<ISurface> surface = mClient->createSurface(&data, pid,name,

display, w, h,format, flags);

if(surface != 0) {

if (uint32_t(data.token) < NUM_LAYERS_MAX) {

//以返回的ISurface对象创建一个SurfaceControl对象

result = new SurfaceControl(this, surface, data, w, h,

format, flags);

}

}

}

returnresult;//返回的是SurfaceControl对象

}

请求端的处理比較简单:

·  调用跨进程的createSurface函数,得到一个ISurface对象。依据Binder一章的知识可知。这个对象的真实类型是BpSurface。只是以后统称之为ISurface。

·  以这个ISurface对象为參数。构造一个SurfaceControl对象。

createSurface函数的响应端在SurfaceFlinger进程中,以下去看这个函数。

在Surface系统定义了非常多类型,咱们也中途歇息一下,最好还是来看看和字符串“Surface”有关的有多少个类。权当其为小小的娱乐:

Native层有Surface、ISurface、SurfaceControl、SurfaceComposerClient。

Java层有Surface、SurfaceSession。

上面列出的还仅仅是一部分。后面还有呢!

*&@&*%¥*

(2)分析createSurface的响应端

前面讲过。可把BClient看作是SF的Proxy,它会把来自客户端的请求派发给SF处理,通过代码来看看,是不是这样的?例如以下所看到的:

[-->SurfaceFlinger.cpp]

sp<ISurface> BClient::createSurface(

ISurfaceFlingerClient::surface_data_t* params, int pid,

const String8& name,

DisplayID display, uint32_t w, uint32_t h, PixelFormat format,

uint32_t flags)

{

//果然是交给SF处理,以后我们将跳过BClient这个代理。

return mFlinger->createSurface(mId, pid,name, params, display, w, h,

format, flags);

}

来看createSurface函数,它的目的就是创建一个ISurface对象,只是这中间的玄机还挺多。代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

sp<ISurface>SurfaceFlinger::createSurface(ClientID clientId, int pid,

const String8& name, ISurfaceFlingerClient::surface_data_t* params,

DisplayID d, uint32_t w, uint32_t h, PixelFormat format,

uint32_t flags)

{

sp<LayerBaseClient> layer;//LayerBaseClient是Layer家族的基类

//这里又冒出一个LayerBaseClient的内部类,它也叫Surface,是不是有点头晕了?

sp<LayerBaseClient::Surface> surfaceHandle;

Mutex::Autolock _l(mStateLock);

//依据clientId找到createConnection时增加的那个Client对象

sp<Client> client = mClientsMap.valueFor(clientId);

......

//注意这个id。它的值表示Client创建的是第几个显示层,依据图8-14能够看出,这个id

//同一时候也表示将使用SharedBufferStatck数组的第id个元素。

int32_t id = client->generateId(pid);

//一个Client不能创建多于NUM_LAYERS_MAX个的Layer。

if(uint32_t(id) >= NUM_LAYERS_MAX) {

return surfaceHandle;

}

//依据flags參数来创建不同类型的显示层,我们在8.4.1节介绍过相关知识

switch(flags & eFXSurfaceMask) {

case eFXSurfaceNormal:

if (UNLIKELY(flags & ePushBuffers)) {

//创建PushBuffer类型的显示层,我们将在拓展思考部分分析它

layer = createPushBuffersSurfaceLocked(client, d, id,

w, h, flags);

} else {

//①创建Normal类型的显示层,我们分析待会这个

layer = createNormalSurfaceLocked(client, d, id,

w, h, flags, format);

}

break;

case eFXSurfaceBlur:

//创建Blur类型的显示层

layer = createBlurSurfaceLocked(client, d, id, w, h, flags);

break;

case eFXSurfaceDim:

//创建Dim类型的显示层

layer = createDimSurfaceLocked(client, d, id, w, h, flags);

break;

}

if(layer != 0) {

layer->setName(name);

setTransactionFlags(eTransactionNeeded);

//从显示层对象中取出一个ISurface对象赋值给SurfaceHandle

surfaceHandle = layer->getSurface();

if(surfaceHandle != 0) {

params->token = surfaceHandle->getToken();

params->identity = surfaceHandle->getIdentity();

params->width = w;

params->height = h;

params->format = format;

}

}

returnsurfaceHandle;//ISurface的Bn端就是这个对象。

}

上面代码中的函数倒是非常easy,知识代码里面冒出来的几个新类型和它们的名字却让人有点头晕。先用文字总结一下:

·  LayerBaseClient:前面提到的显示层在代码中的相应物,就是这个LayerBaseClient。只是这是一个大家族,不同类型的显示层将创建不同类型的LayerBaseClient。

·  LayerBaseClient中有一个内部类。名字叫Surface。这是一个支持Binder通信的类,它派生于ISurface。

关于Layer的故事,后面会有单独的章节来介绍。

这里先继续分析createNormalSurfaceLocked函数。它的代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

sp<LayerBaseClient>SurfaceFlinger::createNormalSurfaceLocked(

const sp<Client>& client, DisplayID display,

int32_t id, uint32_t w, uint32_t h, uint32_t flags,

PixelFormat& format)

{

switch(format) { //一些图像方面的參数设置。能够不去管它。

casePIXEL_FORMAT_TRANSPARENT:

casePIXEL_FORMAT_TRANSLUCENT:

format = PIXEL_FORMAT_RGBA_8888;

break;

casePIXEL_FORMAT_OPAQUE:

format = PIXEL_FORMAT_RGB_565;

break;

}

//①创建一个Layer类型的对象

sp<Layer> layer = new Layer(this, display,client, id);

//②设置Buffer

status_t err = layer->setBuffers(w, h, format, flags);

if (LIKELY(err == NO_ERROR)) {

//初始化这个新layer的一些状态

layer->initStates(w, h, flags);

//③ 还记得在图8-10中提到的Z轴吗?以下这个函数把这个layer增加到Z轴大军中。

addLayer_l(layer);

}

......

returnlayer;

}

createNormalSurfaceLocked函数有三个关键点。它们是:

·  构造一个Layer对象。

·  调用Layer对象的setBuffers函数。

·  调用SF的addLayer_l函数。

暂且记住这三个关键点。后文有单独章节分析它们。

先继续分析SurfaceControl的流程。

(3)创建SurfaceControl对象

当跨进程的createSurface调用返回一个ISurface对象时,将通过以下的代码创建一个SurfaceControl对象:

result = new SurfaceControl(this, surface, data,w, h,format, flags);

以下来看这个SurfaceControl对象为何物。它的代码例如以下所看到的:

[-->SurfaceControl.cpp]

SurfaceControl::SurfaceControl(

const sp<SurfaceComposerClient>& client,

const sp<ISurface>& surface,

const ISurfaceFlingerClient::surface_data_t& data,

uint32_t w, uint32_t h, PixelFormat format, uint32_t flags)

//mClient为SurfaceComposerClient。而mSurface指向跨进程createSurface调用

//返回的ISurface对象。

:mClient(client), mSurface(surface),

mToken(data.token), mIdentity(data.identity),

mWidth(data.width), mHeight(data.height), mFormat(data.format),

mFlags(flags)

{

}

SurfaceControl类能够看作是一个wrapper类:

它封装了一些函数,通过这些函数能够方便地调用mClient或ISurface提供的函数。

在SurfaceControl的分析过程中,还遗留了和Layer相关的部分,以下就来解决它们。

2. Layer和它的家族

我们在createSurface中创建的是Normal的Layer。以下先看这个Layer的构造函数。

(1)Layer的构造

Layer是从LayerBaseClient派生的,其代码例如以下所看到的:

[-->Layer.cpp]

Layer::Layer(SurfaceFlinger* flinger, DisplayIDdisplay,

const sp<Client>& c, int32_t i)//这个i表示SharedBufferStack数组的索引

:   LayerBaseClient(flinger, display, c, i),//先调用基类构造函数

mSecure(false),

mNoEGLImageForSwBuffers(false),

mNeedsBlending(true),

mNeedsDithering(false)

{

//getFrontBuffer实际取出的是FrontBuffer的位置

mFrontBufferIndex = lcblk->getFrontBuffer();

}

再来看基类LayerBaseClient的构造函数,代码例如以下所看到的:

[-->LayerBaseClient.cpp]

LayerBaseClient::LayerBaseClient(SurfaceFlinger*flinger, DisplayID display,

const sp<Client>& client, int32_t i)

:LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i),

mIdentity(uint32_t(android_atomic_inc(&sIdentity)))

{

/*

创建一个SharedBufferServer对象,注意它使用了SharedClient对象,

而且传入了表示SharedBufferStack数组索引的i和一个常量NUM_BUFFERS

*/

lcblk = new SharedBufferServer(

client->ctrlblk, i, NUM_BUFFERS,//该值为常量2,在Layer.h中定义

mIdentity);

}

SharedBufferServer是什么?它和SharedClient有什么关系?

事实上。之前在介绍SharedClient时曾提过与此相关的内容。这里再来认识一下,先看图8-15:

图8-15  ShardBufferServer的示意图

依据上图并结合前面的介绍,能够得出下面结论:

·  在SF进程中。Client的一个Layer将使用SharedBufferStack数组中的一个成员,并通过SharedBufferServer结构来控制这个成员,我们知道SF是消费者,所以可由SharedBufferServer来控制数据的读取。

·  与之相相应,客户端的进程也会有一个对象来使用这个SharedBufferStatck,可它是通过另外一个叫SharedBufferClient的结构来控制的。

客户端为SF提供数据。所以可由SharedBufferClient控制数据的写入。在后文的分析中还会碰到SharedBufferClient。

注意,在拓展思考部分。会有单独章节来分析生产/消费过程中的读写控制。

通过前面的代码可知,Layer对象被new出来后,传给了一个sp对象。读者还记得sp中的onFirstRef函数吗?Layer家族在这个函数中另一些处理。一起去看看,但这个函数由基类LayerBaseClient实现。

[-->LayerBase.cpp]

void LayerBaseClient::onFirstRef()

{

sp<Client> client(this->client.promote());

if (client != 0) {

//把自己增加client对象的mLayers数组中,这部分内容比較简单,读者能够自行研究

client->bindLayer(this, mIndex);

}

}

好。Layer创建完毕,以下来看第二个重要的函数setBuffers。

(2)setBuffers的分析

setBuffers,Layer类以及Layer的基类都有实现。由于创建的是Layer类型的对象。所以请读者直接到Layer.cpp中寻找setBuffers函数。这个函数的目的就是创建用于PageFlipping的FrontBuffer和BackBuffer。一起来看。代码例如以下所看到的:

[-->Layer.cpp]

status_t Layer::setBuffers( uint32_t w, uint32_th,

PixelFormat format,uint32_t flags)

{

PixelFormatInfo info;

status_t err = getPixelFormatInfo(format, &info);

if(err) return err;

//DisplayHardware是代表显示设备的HAL对象,0代表第一块屏幕的显示设备。

//这里将从HAL中取出一些和显示相关的信息。

constDisplayHardware& hw(graphicPlane(0).displayHardware());

uint32_t const maxSurfaceDims = min(

hw.getMaxTextureSize(), hw.getMaxViewportDims());

PixelFormatInfo displayInfo;

getPixelFormatInfo(hw.getFormat(),&displayInfo);

constuint32_t hwFlags = hw.getFlags();

......

/*

创建Buffer,这里将创建两个GraphicBuffer。这两个GraphicBuffer就是我们前面

所说的FrontBuffer和BackBuffer。

*/

for (size_t i=0 ; i<NUM_BUFFERS ; i++) {

//注意,这里调用的是GraphicBuffer的无參构造函数,mBuffers是一个二元数组。

mBuffers[i] = new GraphicBuffer();

}

//又冒出来一个SurfaceLayer类型,#¥%……&*!@

mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);

returnNO_ERROR;

}

setBuffers函数的工作内容比較简单,就是:

·  创建一个GraphicBuffer缓冲数组,元素个数为2,即FrontBuffer和BackBuffer。

·  创建一个SurfaceLayer,关于它的身世我们兴许再介绍。

GraphicBuffer是Android提供的显示内存管理类。关于它的故事,将在8.4.7节中介绍。我们暂把它当做普通的Buffer就可以。

setBuffers中出现的SurfaceLayer类是什么?读者可能对此感觉有些晕乎。待把最后一个关键函数addLayer_l介绍完。也许就不太晕了。

(3)addLayer_l的分析

addLayer_l把这个新创建的layer增加自己的Z轴大军,以下来看:

[-->SurfaceFlinger.cpp]

status_t SurfaceFlinger::addLayer_l(constsp<LayerBase>& layer)

{

/*

mCurrentState是SurfaceFlinger定义的一个结构,它有一个成员变量叫

layersSortedByZ,事实上就是一个排序数组。

以下这个add函数将把这个新的layer依照

它在Z轴的位置增加到排序数组中。mCurrentState保存了全部的显示层。

*/

ssize_t i = mCurrentState.layersSortedByZ.add(

layer,&LayerBase::compareCurrentStateZ);

sp<LayerBaseClient> lbc =

LayerBase::dynamicCast< LayerBaseClient*>(layer.get());

if(lbc != 0) {

mLayerMap.add(lbc->serverIndex(), lbc);

}

returnNO_ERROR;

}

对Layer的三个关键函数都已分析过了,以下正式介绍Layer家族。

(4)Layer家族的介绍

前面的内容确让人头晕眼花,如今应该帮大家恢复清楚的头脑。先来“一剂猛药”。见图8-16:

图8-16  Layer家族

通过上图可知:

·  LayerBaseClient从LayerBase类派生。

·  LayerBaseClient还有四个派生类。各自是Layer、LayerBuffer、LayerDim和LayerBlur。

·  LayerBaseClient定义了一个内部类Surface,这个Surface从ISurface类派生,它支持Binder通信。

·  针对不同的类型。Layer和LayerBuffer分别有一个内部类SurfaceLayer和SurfaceLayerBuffer,它们继承了LayerBaseClient的Surface类。

所以对于Normal类型的显示层来说,getSurface返回的ISurface对象的真正类型是SurfaceLayer。

·  LayerDim和LayerBlur类未定义自己的内部类,所以对于这两种类型的显示层来说,它们直接使用了LayerBaseClient的Surface。

·  ISurface接口提供了非常easy的函数,如requestBuffer、postBuffer等。

这里大量使用了内部类。我们知道,内部类终于都会把请求派发给外部类对象来处理,既然如此。在以后分析中,假设没有特殊情况,就会直接跳到外部类的处理函数中。

强烈建议Google把Surface相关代码好好整理一下,至少让类型名取得更直观些,如今这样确实有点让人头晕。

好。来小小娱乐一下。看之前介绍的和“Surface”有关的名字:

Native层有Surface、ISurface、SurfaceControl、SurfaceComposerClient。

Java层有Surface、SurfaceSession。

在介绍完Layer家族后,与它相关的名字又多了几个。它们是

LayerBaseClient::Surface、Layer::SurfaceLayer、LayerBuffer::SurfaceLayerBuffer。

3. SurfaceControl总结

SurfaceControl创建后得到了什么呢?可用图8-17来表示:

图8-17  SurfaceControl创建后的结果图

通过上图能够知道:

·  mClient成员变量指向SurfaceComposerClient。

·  mSurface的Binder通信响应端为SurfaceLayer。

·  SurfaceLayer有一个变量mOwner指向它的外部类Layer。而Layer有一个成员变量mSurface指向SurfaceLayer。这个SurfaceLayer对象由getSurface函数返回。

注意。mOwner变量由SurfaceLayer的基类Surface(LayBaseClient的内部类)定义。

接下来就是writeToParcel分析和Native Surface对象的创建了。注意。这个Native的Surface可不是LayBaseClient的内部类Surface。

8.4.4  writeToParcel和Surface对象的创建

从乾坤大挪移的知识可知。前面创建的全部对象都在WindowManagerService所在的进程system_server中,而writeToParcel则须要把一些信息打包到Parcel后,发送到Activity所在的进程。究竟哪些内容须要回传给Activity所在的进程呢?

后文将Activity所在的进程简称为Activity端。

1. writeToParcel分析

writeToParcel比較简单,就是把一些信息写到Parcel中去。代码例如以下所看到的:

[-->SurfaceControl.cpp]

status_t SurfaceControl::writeSurfaceToParcel(

const sp<SurfaceControl>& control, Parcel* parcel)

{

uint32_t flags = 0;

uint32_t format = 0;

SurfaceID token = -1;

uint32_t identity = 0;

uint32_t width = 0;

uint32_t height = 0;

sp<SurfaceComposerClient> client;

sp<ISurface> sur;

if(SurfaceControl::isValid(control)) {

token    = control->mToken;

identity= control->mIdentity;

client   = control->mClient;

sur      = control->mSurface;

width    = control->mWidth;

height   = control->mHeight;

format   = control->mFormat;

flags    = control->mFlags;

}

//SurfaceComposerClient的信息须要传递到Activity端。这样客户端那边会构造一个

//SurfaceComposerClient对象

parcel->writeStrongBinder(client!=0  ?

client->connection() : NULL);

//把ISurface对象信息也写到Parcel中,这样Activity端那边也会构造一个ISurface对象

parcel->writeStrongBinder(sur!=0?

sur->asBinder(): NULL);

parcel->writeInt32(token);

parcel->writeInt32(identity);

parcel->writeInt32(width);

parcel->writeInt32(height);

parcel->writeInt32(format);

parcel->writeInt32(flags);

returnNO_ERROR;

}

Parce包发到Activity端后,readFromParcel将依据这个Parcel包构造一个Native的Surface对象,一起来看相关代码。

2. 分析Native的Surface创建过程

[-->android_view_Surface.cpp]

static void Surface_readFromParcel(

JNIEnv* env, jobject clazz, jobject argParcel)

{

Parcel* parcel = (Parcel*)env->GetIntField( argParcel, no.native_parcel);

const sp<Surface>& control(getSurface(env,clazz));

//依据服务端的parcel信息来构造客户端的Surface

sp<Surface> rhs = new Surface(*parcel);

if(!Surface::isSameSurface(control, rhs)) {

setSurface(env, clazz, rhs);

}

}

Native的Surface是怎么利用这个Parcel包的?代码例如以下所看到的:

[-->Surface.cpp]

Surface::Surface(const Parcel& parcel)

:mBufferMapper(GraphicBufferMapper::get()),

mSharedBufferClient(NULL)

{

/*

Surface定义了一个mBuffers变量。它是一个sp<GraphicBuffer>的二元数组。也就是说Surface也存在二个GraphicBuffer。而之前在创建Layer的时候也有两个GraphicBuffer,难道一共同拥有四个GraphicBuffer?这个问题。后面再解答。

*/

sp<IBinder> clientBinder =parcel.readStrongBinder();

//得到ISurface的Bp端BpSurface。

mSurface    =interface_cast<ISurface>(parcel.readStrongBinder());

mToken      = parcel.readInt32();

mIdentity   = parcel.readInt32();

mWidth      = parcel.readInt32();

mHeight     = parcel.readInt32();

mFormat     = parcel.readInt32();

mFlags      = parcel.readInt32();

if (clientBinder != NULL) {

/*

依据ISurfaceFlingerClient对象构造一个SurfaceComposerClient对象,注意我们

如今位于Activity端。这里还没有创建SurfaceComposerClient对象,所以须要创建一个

*/

mClient = SurfaceComposerClient::clientForConnection(clientBinder);

//SharedBuffer家族的最后一员ShardBufferClient终于出现了。

mSharedBufferClient = new SharedBufferClient(

mClient->mControl, mToken, 2,mIdentity);

}

init();//做一些初始化工作。

}

在Surface创建完后。得到什么了呢?看图8-18就可知道:

图8-18  Native Surface的示意图

上图非常清楚地说明:

·  ShardBuffer家族依托共享内存结构SharedClient与它共同组成了Surface系统生产/消费协调的中枢控制机构,它在SF端的代表是SharedBufferServer,在Activity端的代表是SharedBufferClient。

·  Native的Surface将和SF中的SurfaceLayer建立Binder联系。

另外。图中还特意画出了承载数据的GraphicBuffer数组,在代码的凝视中也针对GraphicBuffer提出了一个问题:Surface中有两个GraphicBuffer,Layer也有两个,一共就有四个GraphicBuffer了,可是为什么这里仅仅画出两个呢?

答案是。咱们不是有共享内存吗?这四个GraphicBuffer事实上操纵的是同一段共享内存。所以为了简单。就仅仅画了两个GraphicBuffer。

在8.4.7节再介绍GraphicBuffer的故事。

以下,来看中枢控制机构的SharedBuffer家族。

3. SharedBuffer家族介绍

(1)SharedBuffer家族成员

SharedBuffer是一个家族名称,它包括多少成员呢?来看SharedBuffer的家族图谱。如图8-19所看到的:

图8-19  SharedBuffer家族介绍

从上图能够知道:

·  XXXCondition、XXXUpdate等都是内部类,它们主要是用来更新读写位置的。只是这些操作。为什么要通过类来封装呢?由于SharedBuffer的非常多操作都使用了C++中的Function Object(函数对象),而这些内部类的实例就是函数对象。函数对象是什么?它怎么使用?对此。在以后的分析中会介绍。

(2)SharedBuffer家族和SharedClient的关系

前面介绍过,SharedBufferServer和SharedBufferClient控制的事实上仅仅是SharedBufferStack数组中的一个,以下通过SharedBufferBase的构造函数。来看是否如此。

[-->SharedBufferStack.cpp]

SharedBufferBase::SharedBufferBase(SharedClient*sharedClient,

int surface, int num, int32_t identity)

: mSharedClient(sharedClient),

mSharedStack(sharedClient->surfaces+ surface),

mNumBuffers(num), //依据前面PageFlipping的知识可知。num值为2

mIdentity(identity)

{

/*

上面的赋值语句中最重要的是第二句:

mSharedStack(sharedClient->surfaces +surface)

这条语句使得这个SharedBufferXXX对象,和SharedClient中SharedBufferStack数组

的第surface个元素建立了关系

*/

}

4. Native Surface总结

至此,Activity端Java的Surface对象,终于和一个Native Surface对象挂上了钩,而且这个Native Surface还准备好了画图所需的一切,当中包括:

·  两个GraphicBuffer。这就是PageFlipping所须要的FrontBuffer和BackBuffer。

·  SharedBufferServer和SharedBufferClient结构,这两个结构将用于生产/消费的过程控制。

·  一个ISurface对象,这个对象连接着SF中的一个SurfaceLayer对象。

·  一个SurfaceComposerClient对象,这个对象连接着SF中的一个BClient对象。

资源都已经准备好了。能够開始绘制UI了。以下。分析两个关键的函数lockCanvas和unlockCanvasAndPost。

8.4.5  lockCanvas和unlockCanvasAndPost的分析

这一节,分析精简流程中的最后两个函数lockCanvas和unlockCanvasAndPost。

1. lockCanvas分析

据前文分析可知。UI在绘制前都须要通过lockCanvas得到一块存储空间。也就是所说的BackBuffer。这个过程中终于会调用Surface的lock函数。

其代码例如以下所看到的:

[-->Surface.cpp]

status_t Surface::lock(SurfaceInfo* other,Region* dirtyIn, bool blocking)

{

//传入的參数中。other用来接收一些返回信息,dirtyIn表示须要重绘的区域

......

if (mApiLock.tryLock() != NO_ERROR) {//多线程的情况下要锁住

......

returnWOULD_BLOCK;

}

//设置usage标志。这个标志在GraphicBuffer分配缓冲时有指导作用

setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);

//定义一个GraphicBuffer,名字就叫backBuffer。

sp<GraphicBuffer>backBuffer;

//①还记得我们说的2个元素的缓冲队列吗?以下的dequeueBuffer将取出一个空暇缓冲

status_terr = dequeueBuffer(&backBuffer);

if (err== NO_ERROR) {

//② 锁住这块buffer

err = lockBuffer(backBuffer.get());

if(err == NO_ERROR) {

const Rect bounds(backBuffer->width, backBuffer->height);

Region scratch(bounds);

Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch);

......

//mPostedBuffer是上一次绘画时使用的Buffer,也就是如今的frontBuffer

const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);

if (frontBuffer !=0 &&

backBuffer->width  ==frontBuffer->width &&

backBuffer->height == frontBuffer->height &&

!(mFlags & ISurfaceComposer::eDestroyBackbuffer))

{

const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));

if (!copyback.isEmpty() && frontBuffer!=0) {

/③把frontBuffer中的数据复制到BackBuffer中。这是为什么?

copyBlt(backBuffer,frontBuffer, copyback);

}

}

mDirtyRegion = newDirtyRegion;

mOldDirtyRegion = newDirtyRegion;

void* vaddr;

//调用GraphicBuffer的lock得到一块内存,内存地址被赋值给了vaddr,

//兴许的作画将在这块内存上展开

status_t res = backBuffer->lock(

GRALLOC_USAGE_SW_READ_OFTEN |GRALLOC_USAGE_SW_WRITE_OFTEN,

newDirtyRegion.bounds(),&vaddr);

mLockedBuffer = backBuffer;

//other用来接收一些信息。

other->w      =backBuffer->width;  //宽度信息

other->h      =backBuffer->height;

other->s      =backBuffer->stride;

other->usage  =backBuffer->usage;

other->format = backBuffer->format;

other->bits   = vaddr; //最重要的是这个内存地址

}

}

mApiLock.unlock();

returnerr;

}

在上面的代码中,列出了三个关键点:

·  调用dequeueBuffer得到一个空暇缓冲。也能够叫空暇缓冲出队。

·  调用lockBuffer。

·  调用copyBlt函数,把frontBuffer数据复制到backBuffer中,这是为什么?

来分析这三个关键点。

(1)dequeueBuffer的分析

dequeueBuffer的目的非常easy。就是选取一个空暇的GraphicBuffer,其代码例如以下所看到的:

[-->Surface.cpp]

status_tSurface::dequeueBuffer(sp<GraphicBuffer>* buffer) {

android_native_buffer_t* out;

status_t err = dequeueBuffer(&out);//调用另外一个dequeueBuffer

if(err == NO_ERROR) {

*buffer = GraphicBuffer::getSelf(out);

}

returnerr;

}

这当中又调用了另外一个dequeueBuffer函数。

它的代码例如以下所看到的:

[-->Surface.cpp]

intSurface::dequeueBuffer(android_native_buffer_t** buffer)

{

sp<SurfaceComposerClient> client(getClient());

//①调用SharedBufferClient的dequeue函数,它返回当前空暇的缓冲号

ssize_tbufIdx = mSharedBufferClient->dequeue();

const uint32_t usage(getUsage());

/*

mBuffers就是我们前面在Surface创建中介绍的那个二元sp<GraphicBuffer>数组。

这里定义的backBuffer是一个引用类型,也就是说假设改动backBuffer的信息,

就相当于改动了mBuffers[bufIdx]

*/

const sp<GraphicBuffer>&backBuffer(mBuffers[bufIdx]);

//mBuffers定义的GraphicBuffer使用的也是无參构造函数,所以此时还没有真实的存储被创建

if(backBuffer == 0 || //第一次进来满足backBuffer为空这个条件

((uint32_t(backBuffer->usage) & usage) != usage) ||

mSharedBufferClient->needNewBuffer(bufIdx))

{

//调用getBufferLocked,须要进去看看。

err = getBufferLocked(bufIdx, usage);

if(err == NO_ERROR) {

mWidth  =uint32_t(backBuffer->width);

mHeight = uint32_t(backBuffer->height);

}

}

......

}

上面列出了一个关键点。就是SharedBufferClient的dequeue函数,暂且记住这个调用,后面会有单独章节分析生产/消费步调控制。先看getBufferLocked函数,其代码例如以下所看到的:

[-->Surface.cpp]

tatus_t Surface::getBufferLocked(int index, intusage)

{

sp<ISurface> s(mSurface);

status_t err = NO_MEMORY;

//注意这个currentBuffer也被定义为引用类型

sp<GraphicBuffer>&currentBuffer(mBuffers[index]);

//终于用上了ISurface对象,调用它的requestBuffer得到指定索引index的Buffer

sp<GraphicBuffer> buffer =s->requestBuffer(index, usage);

if (buffer != 0) {

err = mSharedBufferClient->getStatus();

if(!err && buffer->handle != NULL) {

//getBufferMapper返回GraphicBufferMapper对象

//调用它的registerBuffer干什么?这个问题我们在8.4.7节回答

err = getBufferMapper().registerBuffer(buffer->handle);

if (err == NO_ERROR) {

//把requestBuffer得到的值赋给currentBuffer,由于currentBuffer是引用类型,

//实际上相当于mBuffers[index]=buffer

currentBuffer = buffer;

//设置currentBuffer的编号

currentBuffer->setIndex(index);

mNeedFullUpdate = true;

}

}else {

err = err<0 ?

err : NO_MEMORY;

}

return err;

}

至此,getBufferLocked的目的,已比較清楚了:

·  调用ISurface的requestBuffer得到一个GraphicBuffer对象,这个GraphicBuffer对象被设置到本地的mBuffers数组中。

看来Surface定义的这两个GraphicBuffer和Layer定义的两个GraphicBuffer是有联系的,所以图8-18中仅仅画了两个GraphicBuffer。

我们已经知道,ISurface的Bn端实际上是定义在Layer.类中的SurfaceLayer。以下来看它实现的requestBuffer。

由于SurfaceLayer是Layer的内部类,它的工作终于都会交给Layer来处理,所以这里可直接看Layer的requestBuffer函数:

[-->Layer.cpp]

sp<GraphicBuffer> Layer::requestBuffer(intindex, int usage)

{

sp<GraphicBuffer> buffer;

sp<Client> ourClient(client.promote());

//lcblk就是那个SharedBufferServer对象,以下这个调用确保index号GraphicBuffer

//没有被SF当做FrontBuffer使用。

status_t err = lcblk->assertReallocate(index);

......

if(err != NO_ERROR) {

return buffer;

}

uint32_t w, h;

{

Mutex::Autolock _l(mLock);

w= mWidth;

h= mHeight;

/*

mBuffers是SF端创建的一个二元数组,这里取出第index个元素。之前说过,

mBuffers使用的也是GraphicBuffer的无參构造函数,所以此时也没有真实存储被创建。

*/

buffer = mBuffers[index];

mBuffers[index].clear();

}

constuint32_t effectiveUsage = getEffectiveUsage(usage);

if(buffer!=0 && buffer->getStrongCount() == 1) {

//①分配物理存储,后面会分析这个。

err = buffer->reallocate(w, h, mFormat, effectiveUsage);

} else{

buffer.clear();

//使用GraphicBuffer的有參构造,这也使得物理存储被分配

buffer = new GraphicBuffer(w, h, mFormat, effectiveUsage);

err = buffer->initCheck();

}

......

if(err == NO_ERROR && buffer->handle != 0) {

Mutex::Autolock _l(mLock);

if(mWidth && mHeight) {

mBuffers[index] = buffer;

mTextures[index].dirty = true;

}else {

buffer.clear();

}

}

returnbuffer;//返回

}

无论如何,此时跨进程的这个requestBuffer返回的GraphicBuffer,已经和一块物理存储绑定到一起了。所以dequeueBuffer顺利返回了它所需的东西。接下来则需调用lockBuffer。

(2)lockBuffer的分析

lockBuffer的代码例如以下所看到的:

[-->Surface.cpp]

int Surface::lockBuffer(android_native_buffer_t*buffer)

{

sp<SurfaceComposerClient> client(getClient());

status_t err = validate();

int32_t bufIdx = GraphicBuffer::getSelf(buffer)->getIndex();

err =mSharedBufferClient->lock(bufIdx); //调用SharedBufferClient的lock

return err;

}

来看这个lock函数:

[-->SharedBufferStack.cpp]

status_t SharedBufferClient::lock(int buf)

{

LockCondition condition(this, buf);//这个buf是BackBuffer的索引號

status_t err = waitForCondition(condition);

returnerr;

}

注意,给waitForCondition函数传递的是一个LockCondition类型的对象。前面所说的函数对象的作用将在这里见识到,先看waitForCondition函数:

[-->SharedBufferStack.h]

template <typename T> //这是一个模板函数

status_t SharedBufferBase::waitForCondition(Tcondition)

{

constSharedBufferStack& stack( *mSharedStack );

SharedClient& client( *mSharedClient );

constnsecs_t TIMEOUT = s2ns(1);

Mutex::Autolock _l(client.lock);

while((condition()==false) && //注意这个condition()的使用方法

(stack.identity == mIdentity) &&

(stack.status == NO_ERROR))

{

status_t err = client.cv.waitRelative(client.lock, TIMEOUT);

if(CC_UNLIKELY(err != NO_ERROR)) {

if (err == TIMED_OUT) {

if (condition()) {//注意这个:condition()。condition是一个对象

break;

} else {

}

} else {

return err;

}

}

}

return(stack.identity != mIdentity) ? status_t(BAD_INDEX) : stack.status;

}

waitForCondition函数比較简单,就是等待一个条件为真,这个条件是否满足由condition()这条语句来推断。

但这个condition不是一个函数,而是一个对象,这又是怎么回事?

这就是Funcition Object(函数对象)的概念。函数对象的本质是一个对象,只是是重载了操作符()。这和重载操作符+、-等没什么差别。能够把它当作是一个函数来看待。

为什么须要函数对象呢?由于对象能够保存信息。所以调用这个对象的()函数就能够利用这个对象的信息了。

来看condition对象的()函数。

刚才传进来的是LockCondition。它的()定义例如以下:

[-->SharedBufferStack.cpp]

boolSharedBufferClient::LockCondition::operator()() {

//stack、buf等都是这个对象的内部成员,这个对象的目的就是依据读写位置推断这个buffer是

//否空暇。

return(buf != stack.head ||

(stack.queued > 0 && stack.inUse != buf));

}

SharedBufferStack的读写控制。比Audio中的环形缓冲看起来要简单,实际上它却比較复杂。

本章会在扩展部分进行分析。这里给读者准备一个问题。也是我之前百思不得其解的问题:

既然已经调用dequeue得到了一个空暇缓冲,为什么这里还要lock呢?

(3)拷贝旧数据

在第三个关键点中,可看到这样的代码:

[-->Surface.cpp]

status_t Surface::lock(SurfaceInfo* other,Region* dirtyIn, bool blocking)

{

......

const sp<GraphicBuffer>& frontBuffer(mPostedBuffer);

if (frontBuffer !=0 &&

backBuffer->width  ==frontBuffer->width &&

backBuffer->height == frontBuffer->height &&

!(mFlags & ISurfaceComposer::eDestroyBackbuffer))

{

const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion));

if (!copyback.isEmpty() && frontBuffer!=0) {

//③把frontBuffer中的数据复制到BackBuffer中,这是为什么?

copyBlt(backBuffer,frontBuffer, copyback);

}

}

......

}

上面这段代码所解决的,事实上是以下这个问题:

在大部分情况下。UI仅仅有一小部分会发生变化(比如一个button被按下去,导致颜色发生变化),这一小部分UI仅仅相应整个GraphicBuffer中的一小块存储(就是在前面代码中见到的dirtyRegion)。假设整块存储都更新,则会极大地浪费资源。怎么办?

这就须要将变化的图像和没有发生变化的图像进行叠加。

上一次绘制的信息保存在mPostedBuffer中,而这个mPostedBuffer则要在unLockAndPost函数中设置。这里将依据须要。把mPostedBuffer中的旧数据复制到BackBuffer中。兴许的绘画仅仅要更新脏区域就能够了,这会节约不少资源。

OK,lockCanvas返回后,应用层将在这块画布上尽情作画。假设如今已经在BackBuffer上绘制好了图像,以下就要通过unlockCanvasAndPost进行兴许工作了。

一起来看。

2. unlockCanvasAndPost的分析

进入精简流程的最后一步。就是unlockCanvasAndPost函数,它的代码例如以下所看到的:

[-->Surface.cpp]

status_t Surface::unlockAndPost()

{

//调用GraphicBuffer的unlock函数

status_t err = mLockedBuffer->unlock();

//get返回这个GraphicBuffer的编号,queueBuffer将含有新数据的缓冲增加队中。

err =queueBuffer(mLockedBuffer.get());

mPostedBuffer = mLockedBuffer; //保存这个BackBuffer为mPostedBuffer

mLockedBuffer = 0;

returnerr;

}

来看queueBuffer调用,代码例如以下所看到的:

[-->Surface.cpp]

intSurface::queueBuffer(android_native_buffer_t* buffer)

{

sp<SurfaceComposerClient> client(getClient());

int32_t bufIdx =GraphicBuffer::getSelf(buffer)->getIndex();

//设置脏Region

mSharedBufferClient->setDirtyRegion(bufIdx,mDirtyRegion);

//更新写位置。

err =mSharedBufferClient->queue(bufIdx);

if (err== NO_ERROR) {

//client是BpSurfaceFlinger,调用它的signalServer,这样SF就知道新数据准备好了

client->signalServer();

}

returnerr;

}

这里,与读写控制有关的是queue函数,其代码例如以下所看到的:

[-->SharedBufferStack.cpp]

status_t SharedBufferClient::queue(int buf)

{

//QueueUpdate也是一个函数对象

QueueUpdate update(this);

//调用updateCondition函数。

status_t err = updateCondition( update );

SharedBufferStack& stack( *mSharedStack );

constnsecs_t now = systemTime(SYSTEM_TIME_THREAD);

stack.stats.totalTime = ns2us(now - mDequeueTime[buf]);

returnerr;

}

这个updateCondition函数的代码例如以下所看到的:

[-->SharedBufferStack.h]

template <typename T>

status_t SharedBufferBase::updateCondition(Tupdate) {

SharedClient& client( *mSharedClient );

Mutex::Autolock _l(client.lock);

ssize_t result = update();//调用update对象的()函数

client.cv.broadcast(); //触发同步对象

returnresult;

}

updateCondition函数和前面介绍的waitForCondition函数一样,都是使用的函数对象。queue操作使用的是QueueUpdate类。关于它的故事。将在拓展部分讨论。

3. lockCanvas和unlockCanvasAndPost的总结

总结一下lockCanvas和unlockCanvasAndPost这两个函数的工作流程。用图8-20表示:

图8-20  lockCanvas和unlockCanvasAndPost流程总结

8.4.6  GraphicBuffer的介绍

GraphicBuffer是Surface系统中一个高层次的显示内存管理类,它封装了和硬件相关的一些细节,简化了应用层的处理逻辑。先来认识一下它。

1. 初识GraphicBuffer

GraphicBuffer的代码例如以下所看到的:

[-->GraphicBuffer.h]

class GraphicBuffer

:public EGLNativeBase<android_native_buffer_t,

GraphicBuffer,LightRefBase<GraphicBuffer>>,

public Flattenable

当中。EGLNativeBase是一个模板类。

它的定义。代码例如以下所看到的:

[-->Android_natives.h]

template <typename NATIVE_TYPE, typenameTYPE, typename REF>

class EGLNativeBase : public NATIVE_TYPE, publicREF

通过替换。可得到GraphicBuffer的派生关系,如图8-21所看到的:

图8-21  GraphicBuffer派生关系的示意图

从图中能够看出:

·  从LightRefBase派生使GraphicBuffer支持轻量级的引用计数控制。

·  从Flattenable派生使GraphicBuffer支持序列化,它的flatten和unflatten函数用于序列化和反序列化。这样,GraphicBuffer的信息就能够存储到Parcel包中并被Binder传输了。

另外,图中的android_native_buffer_t是GraphicBuffer的父类,它是一个struct结构体。

能够将C++语言中的struct和class当作同一个东西。所以GraphicBuffer能从它派生。其代码例如以下所看到的:

[-->android_native_buffer.h]

typedef struct android_native_buffer_t

{

#ifdef __cplusplus

android_native_buffer_t() {

common.magic = ANDROID_NATIVE_BUFFER_MAGIC;

common.version = sizeof(android_native_buffer_t);

memset(common.reserved, 0, sizeof(common.reserved));

}

#endif

//这个android_native_base_t是struct的第一个成员,依据C/C++编译的特性。这个成员

//在它的派生类对象所占有的内存中也是排第一个。

structandroid_native_base_t common;

intwidth;

intheight;

intstride;

intformat;

intusage;

void* reserved[2];

//这是一个关键成员,保存一些和显示内存分配/管理相关的内容

buffer_handle_t handle;

void*reserved_proc[8];

} android_native_buffer_t;

GraphicBuffer和显示内存分配相关的部分主要集中在buffer_handle_t这个变量上,它实际上是一个指针,定义例如以下:

[-->gralloc.h]

typedef const native_handle* buffer_handle_t;

native_handle的定义例如以下:

[-->native_handle.h]

typedef struct

{

intversion;        /* version值为sizeof(native_handle_t) */

intnumFds;

intnumInts;

intdata[0];        /* data是数据存储空间的首地址 */

} native_handle_t;

typedef native_handle_t native_handle;

读者可能要问,一个小小的GraphicBuffer为什么这么复杂?要回答这个问题,应先对GraphicBuffer有比較全面的了解。

依照图8-20中的流程来看GraphicBuffer。

2. GraphicBuffer和存储的分配

GraphicBuffer的构造函数最有可能分配存储了。注意,流程中使用的是无參构造函数。所以应先看无參构造函数。

(1)无參构造函数的分析

代码例如以下所看到的:

[-->GraphicBuffer.cpp]

GraphicBuffer::GraphicBuffer()

:BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()),

mInitCheck(NO_ERROR), mVStride(0), mIndex(-1)

{

/*

当中mBufferMapper为GraphicBufferMapper类型,它的创建採用的是单例模式,也就是每一个

进程仅仅有一个GraphicBufferMapper对象,读者能够去看看get的实现。

*/

width  =

height=

stride=

format=

usage  = 0;

handle= NULL; //handle为空

}

在无參构造函数中没有发现和存储分配有关的操作。那么。依据流程。下一个有可能的地方就是reallocate函数了。

(2)reallocate的分析

Reallocate的代码例如以下所看到的:

[-->GraphicBuffer.cpp]

status_t GraphicBuffer::reallocate(uint32_t w,uint32_t h, PixelFormat f,

uint32_t reqUsage)

{

if(mOwner != ownData)

return INVALID_OPERATION;

if(handle) {//handle值在无參构造函数中初始化为空,所以不满足if的条件

GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());

allocator.free(handle);

handle = 0;

}

returninitSize(w, h, f, reqUsage);//调用initSize函数

}

InitSize函数的代码例如以下所看到的:

[-->GraphicBuffer.cpp]

status_t GraphicBuffer::initSize(uint32_t w,uint32_t h, PixelFormat format,

uint32_t reqUsage)

{

if(format == PIXEL_FORMAT_RGBX_8888)

format = PIXEL_FORMAT_RGBA_8888;

/*

GraphicBufferAllocator才是真正的存储分配的管理类,它的创建也是採用的单例模式,

也就是每一个进程仅仅有一个GraphicBufferAllocator对象

*/

GraphicBufferAllocator& allocator =GraphicBufferAllocator::get();

//调用GraphicBufferAllocator的alloc来分配存储,注意handle作为指针

//被传了进去。看来handle的值会被改动

status_t err = allocator.alloc(w, h, format, reqUsage, &handle,&stride);

if(err == NO_ERROR) {

this->width  = w;

this->height = h;

this->format = format;

this->usage  = reqUsage;

mVStride = 0;

}

returnerr;

}

(3)GraphicBufferAllocator的介绍

从上面的代码中能够发现,GraphicBuffer的存储分配和GraphicBufferAllocator有关。一个小小的存储分配为什么须要经过这么多道工序呢?还是先来看GraphicBufferAllocator。代码例如以下所看到的:

[-->GraphicBufferAllocator.cpp]

GraphicBufferAllocator::GraphicBufferAllocator()

:mAllocDev(0)

{

hw_module_t const* module;

//调用hw_get_module。得到hw_module_t

interr = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);

if (err == 0) {

//调用gralloc_open函数,注意我们把module參数传了进去。

gralloc_open(module, &mAllocDev);

}

}

GraphicBufferAllocator在创建时,会首先调用hw_get_module取出一个hw_module_t类型的对象。从名字上看。它和硬件平台有关系。它会载入一个叫libgralloc.硬件平台名.so的动态库。比方。我的HTC G7手机上载入的库是/system/lib/hw/libgraolloc.qsd-8k.so。这个库的源代码在hardware/msm7k/libgralloc-qsd8k文件夹下。

这个库有什么用呢?简言之,就是为了分配一块用于显示的内存,但为什么须要这样的层层封装呢?答案非常easy:

封装的目的就是为了屏蔽不同硬件平台的差别。

读者可通过运行adb getprop ro.board.platform命令。得到具体手机上硬件平台的名字。图8-22总结了GraphicBufferAllocator分配内存的途径。

这部分代码,读者可參考hardware/libhardware/hardware.c和hardware/msm7k/libgralloc-qsd8k/gralloc.cpp。后文将不再深入探讨和硬件平台有关的知识。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-22  GraphicBufferAllocator内存的分配途径

注意。这里是以G7的libgralloc.qsk-8k.so为演示样例的。

当中pmem设备用来创建一块连续的内存。由于有些硬件设备(比如Camera)工作时须要使用一块连续的内存,对于这样的情况。一般就会使用pmem设备来分配内存。

这里,仅讨论图8-22中与硬件无关的分配方式。在这样的情况下,将使用ashmem分配共享内存。以下看GraphicBufferAllocator的alloc函数。其代码例如以下所看到的:

[-->GraphicBufferAllocator.cpp]

status_t GraphicBufferAllocator::alloc(uint32_tw, uint32_t h, PixelFormat format,int usage, buffer_handle_t* handle, int32_t*stride)

{

//依据前面的定义可知buffer_handle_t为native_handle_t*类型

status_t err;

if (usage & GRALLOC_USAGE_HW_MASK) {

err =mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride);

} else {

//SW分配,能够做到和HW无关了。

err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride);

}

......

returnerr;

}

以下,来看软件分配的方式:

[-->GraphicBufferAllocator.cpp]

status_t sw_gralloc_handle_t::alloc(uint32_t w,uint32_t h, int format,

int usage, buffer_handle_t* pHandle, int32_t*pStride)

{

intalign = 4;

intbpp = 0;

......//格式转换

size_tbpr = (w*bpp + (align-1)) & ~(align-1);

size_tsize = bpr * h;

size_tstride = bpr / bpp;

size =(size + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);

//直接使用了ashmem创建共享内存

int fd= ashmem_create_region("sw-gralloc-buffer", size);

......

//进行内存映射。得到共享内存起始地址

void*base = mmap(0, size, prot, MAP_SHARED, fd, 0);

sw_gralloc_handle_t* hnd = new sw_gralloc_handle_t();

hnd->fd = fd;//保存文件描写叙述符

hnd->size = size;//保存共享内存的大小

hnd->base = intptr_t(base);//intptr_t将void*类型转换成int*类型

hnd->prot = prot;//保存属性

*pStride = stride;

*pHandle = hnd; //pHandle就是传入的那个handle变量的指针。这里对它进行赋值

returnNO_ERROR;

}

我们知道,调用GraphicBuffer的reallocate函数后。会导致物理存储被分配。前面曾说过,Layer会创建两个GraphicBuffer。而Native Surface端也会创建两个GraphicBuffer。那么这两个GraphicBuffer是怎么建立联系的呢?为什么说native_handle_t是GraphicBuffer的精髓呢?

3. flatten和unflatten的分析

试想,Native Surface的GraphicBuffer是怎么和Layer的GraphicBuffer建立联系的:

先通过requestBuffer函数返回一个GraphicBuffer,然后这个GraphicBuffer被Native Surface保存。

这中间的过程事实上是一个mini版的乾坤挪移,来看看,代码例如以下所看到的:

[-->ISurface.cpp]

//requestBuffer的响应端

status_t BnSurface::onTransact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

switch(code) {

case REQUEST_BUFFER: {

CHECK_INTERFACE(ISurface, data, reply);

int bufferIdx = data.readInt32();

int usage = data.readInt32();

sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage));

......

/*

requestBuffer的返回值被写到Parcel包中,由于GraphicBuffer从

Flattenable类派生,这将导致它的flatten函数被调用

*/

return reply->write(*buffer);

}

.......

}

//再来看请求端的处理。在BpSurface中

virtual sp<GraphicBuffer> requestBuffer(intbufferIdx, int usage)

{

Parcel data, reply;

data.writeInterfaceToken(ISurface::getInterfaceDescriptor());

data.writeInt32(bufferIdx);

data.writeInt32(usage);

remote()->transact(REQUEST_BUFFER, data, &reply);

sp<GraphicBuffer> buffer = new GraphicBuffer();

reply.read(*buffer);//Parcel调用unflatten函数把信息反序列化到这个buffer中。

return buffer;//requestBuffer实际上返回的是本地new出来的这个GraphicBuffer

}

通过上面的代码能够发现,挪移的关键体如今flatten和unflatten函数上。请看:

(1)flatten的分析

flatten的代码例如以下所看到的:

[-->GraphicBuffer.cpp]

status_t GraphicBuffer::flatten(void* buffer,size_t size,

int fds[], size_t count) const

{

//buffer是装载数据的缓冲区,由Parcel提供

......

if(handle) {

buf[6] = handle->numFds;

buf[7] = handle->numInts;

native_handle_t const* const h = handle;

//把handle的信息也写到buffer中

memcpy(fds,     h->data,             h->numFds*sizeof(int));

memcpy(&buf[8], h->data + h->numFds,h->numInts*sizeof(int));

}

returnNO_ERROR;

}

flatten的工作就是把GraphicBuffer的handle变量信息写到Parcel包中。

那么接收端如何使用这个包呢?这就是unflatten的工作了。

(2)unflatten分析

unflatten的代码例如以下所看到的:

[-->GraphicBuffer.cpp]

status_t GraphicBuffer::unflatten(void const*buffer, size_t size,

int fds[], size_t count)

{

......

if(numFds || numInts) {

width  = buf[1];

height = buf[2];

stride = buf[3];

format = buf[4];

usage  = buf[5];

native_handle* h =native_handle_create(numFds, numInts);

memcpy(h->data,         fds,     numFds*sizeof(int));

memcpy(h->data + numFds, &buf[8],numInts*sizeof(int));

handle = h;//依据Parcel包中的数据还原一个handle

} else{

width = height = stride = format = usage = 0;

handle = NULL;

}

mOwner= ownHandle;

returnNO_ERROR;

}

unflatten最重要的工作是。依据Parcel包中native_handle的信息。在Native Surface端构造一个对等的GraphicBuffer。这样。Native Surface端的GraphicBuffer实际上就和Layer端的GraphicBuffer管理着同一块共享内存。

3. registerBuffer的分析

registerBuffer有什么用呢?上一步调用unflatten后得到了代表共享内存的文件句柄。regiserBuffer的目的就是对它进行内存映射,代码例如以下所看到的:

[-->GraphicBufferMapper.cpp]

status_tsw_gralloc_handle_t::registerBuffer(sw_gralloc_handle_t* hnd)

{

if (hnd->pid != getpid()) {

//原来是做一次内存映射操作

void* base = mmap(0, hnd->size, hnd->prot, MAP_SHARED, hnd->fd,0);

......

//base保存着共享内存的起始地址

hnd->base = intptr_t(base);

}

returnNO_ERROR;

}

4. lock和unlock的分析

GraphicBuffer在使用前须要通过lock来得到内存地址,使用完后又会通过unlock释放这块地址。在SW分配方案中,这两个函数实现却非常easy,例如以下所看到的:

[-->GraphicBufferMapper.cpp]

//lock操作

int sw_gralloc_handle_t::lock(sw_gralloc_handle_t*hnd, int usage,

int l, int t, int w, int h, void** vaddr)

{

*vaddr= (void*)hnd->base;//得到共享内存的起始地址,兴许作画就使用这块内存了。

returnNO_ERROR;

}

//unlock操作

status_tsw_gralloc_handle_t::unlock(sw_gralloc_handle_t* hnd)

{

returnNO_ERROR;//没有不论什么操作

}

对GraphicBuffer的介绍就到这里。尽管採用的是SW方式。可是相信读者也能通过树木领略到森林的风採。从应用层角度看。能够把GraphicBuffer当做一个构架在共享内存之上的数据缓冲。

对想深入研究的读者。我建议可按图8-20中的流程来分析。由于流程体现了调用顺序。表达了调用者的意图和目的,仅仅有把握了流程。分析时才不会迷失在茫茫的源代码海洋中,才不会被不熟悉的知识阻拦前进的脚步。

8.4.7深入分析Surface总结

Surface系统最难的部分,是这个Native Surface的创建和使用。它包括三个方面:

·  Activity的UI和Surface的关系是如何的?这是8.2节回答的问题。

·  Activity中所使用的Surface是怎么和SurfaceFlinger挂上关系的?这是8.3节回答的问题。

·  本节对第2个问题进行了较深入的研究。分析了Surface和SurfaceFlinger之间的关系。以及生产/消费步调的中枢控制机构SharedBuffer家族和数据的承载者GraphicBuffer。

从上面分析可看出。本章前四节均环绕着这个Surface解说。一路下来确实遇到了不少曲折和坎坷,望读者跟着源代码重复阅读,体会。

8.5  SurfaceFlinger的分析

这一节要对SurfaceFlinger进行分析。相比較而言,SurfaceFlinger不如AudioFlinger复杂。

8.5.1  SurfaceFlinger的诞生

SurfaceFlinger驻留于system_server进程。这一点和Audio系统的几个Service不太一样。

它创建的位置在SystemServer的init1函数中(第4章4.3.2节的第3点)。尽管位于SystemServer这个重要进程中,可是SF创建的代码却略显波澜不惊。没有什么特别之处。SF的创建首先会调用instantiate函数,代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::instantiate() {

defaultServiceManager()->addService(

String16("SurfaceFlinger"), new SurfaceFlinger());

}

前面在图8-14中指出了SF。同一时候从BnSurfaceComposer和Thread类中派生,相关代码例如以下所看到的:

class SurfaceFlinger : public BnSurfaceComposer,protected Thread

从Thread派生这件事给了我们一个非常明确的提示:

·  SurfaceFlinger会单独启动一个工作线程。

我们知道,Thread类的工作线程要通过调用它的run函数来创建,那这个run函数是在什么地方调用的呢?当然,最有可能的就是在构造函数中:

[-->SurfaceFlinger.cpp]

SurfaceFlinger::SurfaceFlinger()

:   BnSurfaceComposer(), Thread(false),

mTransactionFlags(0),

mTransactionCount(0),

mResizeTransationPending(false),

mLayersRemoved(false),

mBootTime(systemTime()),

mHardwareTest("android.permission.HARDWARE_TEST"),

mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),

mDump("android.permission.DUMP"),

mVisibleRegionsDirty(false),

mDeferReleaseConsole(false),

mFreezeDisplay(false),

mFreezeCount(0),

mFreezeDisplayTime(0),

mDebugRegion(0),

mDebugBackground(0),

mDebugInSwapBuffers(0),

mLastSwapBufferTime(0),

mDebugInTransaction(0),

mLastTransactionTime(0),

mBootFinished(false),

mConsoleSignals(0),

mSecureFrameBuffer(0)

{

init();//上面没有调用run。

必须到init去检查一番。

}

//init函数更简单了。

void SurfaceFlinger::init()

{

charvalue[PROPERTY_VALUE_MAX];

property_get("debug.sf.showupdates", value, "0");

mDebugRegion = atoi(value);

property_get("debug.sf.showbackground", value, "0");

mDebugBackground = atoi(value);

}

嗯?上面的代码居然没有创建工作线程?难道在其它地方?读者别急着在文件里搜索“run”。先推測一下答案。

·  依据之前所学的知识,另外一个最有可能的地方就是onFirstRef函数了。这个函数在对象第一次被sp化后调用。非常多初始化的工作也能够在这个函数中完毕。

事实是这样吗?一起来看。

1. onFirstRef的分析

onFirstRef的代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::onFirstRef()

{

//真是梦里寻他千百度,果然是在onFirstRef中创建了工作线程

run("SurfaceFlinger",PRIORITY_URGENT_DISPLAY);

/*

mReadyToRunBarrier类型为Barrier,这个类就是封装了一个Mutex对象和一个Condition

对象。

假设读者还记得第5章有关同步类的介绍,理解这个Barrier就非常easy了。

以下调用的

wait函数表示要等待一个同步条件的满足。

*/

mReadyToRunBarrier.wait();

}

onFirstRef创建工作线程后。将等待一个同步条件,那么这个同步条件在哪里被触发呢?相信不用多说 大家也知道:

在工作线程中被触发,而且极有可能是在readyToRun函数中。

不清楚Thread类的读者能够复习一下与第5章有关的Thread类的知识。

2. readyToRun的分析

SF的readyToRun函数将完毕一些初始化工作,代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

status_t SurfaceFlinger::readyToRun()

{

intdpy = 0;

{

//①GraphicPlane是什么?

GraphicPlane& plane(graphicPlane(dpy));

//②为这个GraphicPlane设置一个HAL对象——DisplayHardware

DisplayHardware* const hw = new DisplayHardware(this, dpy);

plane.setDisplayHardware(hw);

}

//创建Surface系统中的“CB”对象。依照老规矩。应该先创建一块共享内存,然后使用placment new

mServerHeap = new MemoryHeapBase(4096,

MemoryHeapBase::READ_ONLY,

"SurfaceFlingerread-only heap");

/*

注意这个“CB“对象的类型是surface_flinger_cblk_t。为什么在CB上打引號呢?由于这个对象

谈不上什么控制,仅仅只是被用来存储一些信息罢了。

其控制作用全然达不到audio_track_cblk_t

的程度。基于这样的事实,我们把前面提到的SharedBuffer家族称之为CB对象。

*/

mServerCblk=

static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase());

//placementnew创建surface_flinger_cblk_t

new(mServerCblk) surface_flinger_cblk_t;

constGraphicPlane& plane(graphicPlane(dpy));

constDisplayHardware& hw = plane.displayHardware();

constuint32_t w = hw.getWidth();

constuint32_t h = hw.getHeight();

constuint32_t f = hw.getFormat();

hw.makeCurrent();

//当前仅仅有一块屏

mServerCblk->connected|= 1<<dpy;

//屏幕在“CB”对象中的代表是display_cblk_t

display_cblk_t* dcblk = mServerCblk->displays + dpy;

memset(dcblk, 0, sizeof(display_cblk_t));

dcblk->w            =plane.getWidth();

dcblk->h            =plane.getHeight();

......//获取屏幕信息

//还用上了内联汇编语句。

asmvolatile ("":::"memory");

/*

以下是一些和OpenGL相关的函数调用。读者如感兴趣,能够研究一下。

至少SurfaceFlinger.cpp中所涉及的相关代码还不算难懂

*/

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, 0);

......

glOrthof(0, w, h, 0, 0, 1);

//LayerDim是Dim类型的Layer

LayerDim::initDimmer(this, w, h);

//还记得在onFirstRef函数中的wait吗?以下的open将触发这个同步条件

mReadyToRunBarrier.open();

//资源准备好后。init将启动bootanim程序。这样就见到开机动画了。

property_set("ctl.start", "bootanim");

returnNO_ERROR;

}

在上面的代码中,列出了两个关键点,以下一一进行分析。

(1)GraphicPlane的介绍

GraphicPlane是屏幕在SF代码中的相应物,依据前面的介绍。眼下Android仅仅支持一块屏幕,所以SF定义了一个一元数组:

GraphicPlane     mGraphicPlanes[1];

GraphicPlane虽无什么特别之处,但它有一个重要的函数,叫setDisplayHardware。这个函数把代表显示设备的HAL对象和GraphicPlane关联起来。这也是以下要介绍的第二个关键点DisplayHardware。

(2)DisplayHardware的介绍

从代码上看,这个和显示相关的HAL对象是在工作线程中new出来的,先看它的构造函数,代码例如以下所看到的:

[-->DisplayHardware.cpp]

DisplayHardware::DisplayHardware(

const sp<SurfaceFlinger>& flinger,

uint32_t dpy)

:DisplayHardwareBase(flinger, dpy)

{

init(dpy); //最重要的是这个init函数。

}

init函数非常重要。应进去看看。以下先思考一个问题。

前面在介绍FrameBuffer时说过,显示这一块须要使用FrameBuffer,但在GraphicBuffer中用的却是ashmem创建的共享内存。也就是说。之前在共享内存中绘制的图像和FrameBuffer没有什么关系。那么FrameBuffer是在哪里创建的呢?

答案就在init函数中,代码例如以下所看到的:

[-->DisplayHardware.cpp]

void DisplayHardware::init(uint32_t dpy)

{

//FrameBufferNativeWindow实现了对FrameBuffer的管理和操作,该类中创建了两个

//FrameBuffer,分别起到FrontBuffer和BackBuffer的作用。

mNativeWindow = new FramebufferNativeWindow();

framebuffer_device_t const * fbDev = mNativeWindow->getDevice();

mOverlayEngine = NULL;

hw_module_t const* module;//Overlay相关

if(hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {

overlay_control_open(module, &mOverlayEngine);

}

......

EGLint w, h, dummy;

EGLintnumConfigs=0;

EGLSurface surface;

EGLContext context;

mFlags= CACHED_BUFFERS;

//EGLDisplay在EGL中代表屏幕

EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

......

/*

surface是EGLSurface类型,以下这个函数会将EGL和Android中的Display系统绑定起来,

兴许就能够利用OpenGL在这个Surface上绘画。然后通过eglSwappBuffers输出图像了。

*/

surface= eglCreateWindowSurface(display, config,

mNativeWindow.get(),NULL);

......

mDisplay = display;

mConfig  = config;

mSurface = surface;

mContext = context;

mFormat  = fbDev->format;

mPageFlipCount = 0;

}

依据上面的代码,如今能够回答前面的问题了:

·  SF创建FrameBuffer。并将各个Surface传输的数据(通过GraphicBuffer)混合后,再由自己传输到FrameBuffer中进行显示。

本节的内容,实际上涉及另外一个比Surface更复杂的Display系统。出于篇幅和精力的原因,本书眼下不打算讨论它。

8.5.2  SF工作线程的分析

SF中的工作线程就是来做图像混合的。比起AudioFlinger来。它相当简单,以下是它的代码:

[-->SurfaceFlinger.cpp]

bool SurfaceFlinger::threadLoop()

{

waitForEvent();//① 等待什么事件呢?

if (UNLIKELY(mConsoleSignals)) {

handleConsoleEvents();

}

if(LIKELY(mTransactionCount == 0)) {

const uint32_t mask = eTransactionNeeded | eTraversalNeeded;

uint32_t transactionFlags = getTransactionFlags(mask);

if(LIKELY(transactionFlags)) {

//Transaction(事务)处理。放到本节最后来讨论

handleTransaction(transactionFlags);

}

}

//②处理PageFlipping工作

handlePageFlip();

constDisplayHardware& hw(graphicPlane(0).displayHardware());

if (LIKELY(hw.canDraw() && !isFrozen())) {

//③处理重绘

handleRepaint();

hw.compositionComplete();

//④投递BackBuffer

unlockClients();

postFramebuffer();

} else{

unlockClients();

usleep(16667);

}

returntrue;

}

ThreadLoop一共同拥有四个关键点,这里,分析除Transaction外的三个关键点。

1. waitForEvent

SF工作线程一上来就等待事件,它会是什么事件呢?来看代码:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::waitForEvent()

{

while(true) {

nsecs_t timeout = -1;

const nsecs_t freezeDisplayTimeout = ms2ns(5000);

......

MessageList::value_type msg = mEventQueue.waitMessage(timeout);

......//另一些和冻屏相关的内容

if(msg != 0) {

switch (msg->what) {

//千辛万苦就等这一个重绘消息

case MessageQueue::INVALIDATE:

return;

}

}

}

}

SF收到重绘消息后,将退出等待。那么,是谁发送的这个重绘消息呢?还记得在unlockCanvasAndPost函数中调用的signal吗?它在SF端的实现代码例如以下:

[-->SurfaceFlinger]

void SurfaceFlinger::signal() const {

const_cast<SurfaceFlinger*>(this)->signalEvent();

}

void SurfaceFlinger::signalEvent() {

mEventQueue.invalidate(); //往消息队列中增加INVALIDATE消息

}

2. 分析handlePageFlip

SF工作线程从waitForEvent中返回后,下一步要做的就是处理事务和handlePageFlip了。

先看handlePageFlip,从名字上可知,它和PageFlipping工作有关。

注意:事务处理将在8.5.3节中介绍。

代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::handlePageFlip()

{

bool visibleRegions = mVisibleRegionsDirty;

/*

还记得前面所说的mCurrentState吗?它保存了全部显示层的信息,而绘制的时候使用的

mDrawingState则保存了当前须要显示的显示层信息。

*/

LayerVector& currentLayers =

const_cast<LayerVector&>(mDrawingState.layersSortedByZ);

//①调用lockPageFlip

visibleRegions |= lockPageFlip(currentLayers);

const DisplayHardware& hw =graphicPlane(0).displayHardware();

//取得屏幕的区域

const Region screenRegion(hw.bounds());

if (visibleRegions) {

Region opaqueRegion;

computeVisibleRegions(currentLayers, mDirtyRegion,opaqueRegion);

mWormholeRegion = screenRegion.subtract(opaqueRegion);

mVisibleRegionsDirty = false;

}

//② 调用unlockPageFlip

unlockPageFlip(currentLayers);

mDirtyRegion.andSelf(screenRegion);

}

hanldePageFlip调用了两个看起来是一对的函数:lockPageFlip和unlockPageFlip。这两个函数会干些什么呢?

(1)lockPageFlip的分析

先看lockPageFlip函数,代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

bool SurfaceFlinger::lockPageFlip(constLayerVector& currentLayers)

{

boolrecomputeVisibleRegions = false;

size_tcount = currentLayers.size();

sp<LayerBase> const* layers = currentLayers.array();

for(size_t i=0 ; i<count ; i++) {

const sp<LayerBase>& layer = layers[i];

//调用每一个显示层的lockPageFlip

layer->lockPageFlip(recomputeVisibleRegions);

}

returnrecomputeVisibleRegions;

}

假设当前的显示层是Layer类型,那么得转到Layer类去看它的lockPageFlip函数,代码例如以下所看到的:

[-->Layer.cpp]

void Layer::lockPageFlip(bool&recomputeVisibleRegions)

{

//lcblk是SharedBufferServer类型,调用retireAndLock函数将返回FrontBuffer的

//索引號

ssize_tbuf = lcblk->retireAndLock();

......

mFrontBufferIndex = buf;

//得到FrontBuffer相应的GraphicBuffer

sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));

if (newFrontBuffer != NULL) {

//取出脏区域

const Region dirty(lcblk->getDirtyRegion(buf));

//和GraphicBuffer所表示的区域进行裁剪,得到一个脏区域

mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );

const Layer::State& front(drawingState());

if(newFrontBuffer->getWidth()  ==front.requested_w &&

newFrontBuffer->getHeight() ==front.requested_h)

{

if ((front.w != front.requested_w) ||

(front.h != front.requested_h))

{

...... //须要又一次计算可见区域

recomputeVisibleRegions = true;

}

mFreezeLock.clear();

}

} else{

mPostedDirtyRegion.clear();

}

if(lcblk->getQueuedCount()) {

mFlinger->signalEvent();

}

/*

假设脏区域不为空,则须要绘制成纹理,reloadTexture将绘制一张纹理保存在

mTextures数组中,里边涉及非常多OpenGL的操作,读者有兴趣能够自己研究。

*/

if(!mPostedDirtyRegion.isEmpty()) {

reloadTexture( mPostedDirtyRegion );

}

}

我们知道。Layer的lockPageFlip将依据FrontBuffer的内容生成一张纹理。那么。unlockPageFlip会做些什么呢?

(2)unlockPageFlip的分析

unlockPageFlip的代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::unlockPageFlip(constLayerVector& currentLayers)

{

constGraphicPlane& plane(graphicPlane(0));

constTransform& planeTransform(plane.transform());

size_tcount = currentLayers.size();

sp<LayerBase> const* layers = currentLayers.array();

for(size_t i=0 ; i<count ; i++) {

const sp<LayerBase>& layer = layers[i];

//调用每一个显示层的unlockPageFlip,Layer的unlockPageFlip主要做一些

//区域的清理工作。读者能够自己看看。

layer->unlockPageFlip(planeTransform, mDirtyRegion);

}

}

(3)handlePageFlip的总结

handlePageFlip的工作事实上非常easy,以Layer类型为例来总结一下:

各个Layer须要从FrontBuffer中取得新数据。并生成一张OpenGL中的纹理。纹理能够看做是一个图片,这个图片的内容就是FrontBuffer中的图像。

如今每一个Layer都准备好了新数据,下一步的工作当然就是绘制了。来看handleRepaint函数。

3. 分析handleRepaint函数

handleRepaint函数的代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::handleRepaint()

{

mInvalidRegion.orSelf(mDirtyRegion);

if(mInvalidRegion.isEmpty()) {

return;

}

......

constDisplayHardware& hw(graphicPlane(0).displayHardware());

glMatrixMode(GL_MODELVIEW);

glLoadIdentity();

uint32_t flags = hw.getFlags();

if((flags & DisplayHardware::SWAP_RECTANGLE) ||

(flags & DisplayHardware::BUFFER_PRESERVED))

{

......//计算mDirtyRegion

}

// 在脏区域上进行绘制

composeSurfaces(mDirtyRegion);

mDirtyRegion.clear();

}

当中,composeSurfaces将不同的显示层内容进行混合,事实上就是按Z轴的顺序由里到外依次绘制。当然,最后绘制的数据有可能遮盖前面绘制的数据。代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::composeSurfaces(constRegion& dirty)

{

constSurfaceFlinger& flinger(*this);

constLayerVector& drawingLayers(mDrawingState.layersSortedByZ);

constsize_t count = drawingLayers.size();

sp<LayerBase> const* const layers = drawingLayers.array();

for(size_t i=0 ; i<count ; ++i) {

const sp<LayerBase>& layer = layers[i];

const Region&visibleRegion(layer->visibleRegionScreen);

if(!visibleRegion.isEmpty())  {

const Region clip(dirty.intersect(visibleRegion));

if (!clip.isEmpty()) {

layer->draw(clip); //调用各个显示层的layer函数

}

}

}

}

draw函数在LayerBase类中实现。代码例如以下所看到的:

[-->LayerBase.cpp]

void LayerBase::draw(const Region& inClip)const

{

......

glEnable(GL_SCISSOR_TEST);

onDraw(clip);//调用子类的onDraw函数

}

至于Layer是怎么实现这个onDraw函数的,代码例如以下所看到的:

[-->Layer.cpp]

void Layer::onDraw(const Region& clip) const

{

intindex = mFrontBufferIndex;

if(mTextures[index].image == EGL_NO_IMAGE_KHR)

index = 0;

GLuint textureName = mTextures[index].name;

....

Region holes(clip.subtract(under));

if(!holes.isEmpty()) {

clearWithOpenGL(holes);

}

return;

}

//index是FrontBuffer相应生成的纹理,在lockPageFlip函数中就已经生成了。

drawWithOpenGL(clip,mTextures[index]);//将纹理画上去,里面有非常多和OpenGL相关内容

}

drawWithOpenGL函数由LayerBase实现,看它是不是使用了这张纹理。代码例如以下所看到的:

[-->LayerBase.cpp]

void LayerBase::drawWithOpenGL(const Region&clip, const Texture& texture) const

{

constDisplayHardware& hw(graphicPlane(0).displayHardware());

constuint32_t fbHeight = hw.getHeight();

constState& s(drawingState());

//validateTexture函数内部将绑定指定的纹理

validateTexture(texture.name);

//以下就是OpenGL操作函数了

glEnable(GL_TEXTURE_2D);

......

glMatrixMode(GL_TEXTURE);

glLoadIdentity();

//坐标旋转

switch(texture.transform) {

case HAL_TRANSFORM_ROT_90:

glTranslatef(0, 1, 0);

glRotatef(-90, 0, 0, 1);

break;

case HAL_TRANSFORM_ROT_180:

glTranslatef(1, 1, 0);

glRotatef(-180, 0, 0, 1);

break;

case HAL_TRANSFORM_ROT_270:

glTranslatef(1, 0, 0);

glRotatef(-270, 0, 0, 1);

break;

}

if (texture.NPOTAdjust) {

//缩放处理

glScalef(texture.wScale, texture.hScale, 1.0f);

}

//使能纹理坐标

glEnableClientState(GL_TEXTURE_COORD_ARRAY);

//设置顶点坐标

glVertexPointer(2, GL_FIXED, 0, mVertices);

//设置纹理坐标

glTexCoordPointer(2, GL_FIXED, 0, texCoords);

while(it != end) {

const Rect& r = *it++;

const GLint sy = fbHeight - (r.top + r.height());

//裁剪

glScissor(r.left, sy, r.width(), r.height());

//画矩形

glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

}

//禁止纹理坐标

glDisableClientState(GL_TEXTURE_COORD_ARRAY);

}

纹理绑定是OpenGL的经常使用函数,其代码例如以下所看到的。

[-->LayerBase.cpp]

void LayerBase::validateTexture(GLinttextureName) const

{

//以下这个函数将绑定纹理

glBindTexture(GL_TEXTURE_2D, textureName);

......//其它一些设置

}

handleRepaint这个函数基本上就是按Z轴的顺序对每一层进行重绘。重绘的方法就是使用OpenGL。

我在Android平台上有几个月的OpenGL开发经历,还谈不上非常深刻,当中的一些资料,希望能够给感兴趣的读者提供參考。

1)OpenGL的入门教材当选NeHe的资料。大略看前几章就可以。

2) Android平台上关于OpenGL ES的开发,有一篇非常具体的Word文档叫《OpenGL ESTutorial for Android》。

该文具体描写叙述了在Android平台上进行OpenGL开发的流程。大家可跟着这篇教材,在模拟器上做一些练习。那里面涉及到的一些基础知识,从前面介绍的入门教材中能够学到。

3)有了前面两点的基础后,就须要对整个OpenGL有比較完整深入的了解了。我在那时所看的书是《OpenGL Programming Guide (7th Edition)》。

该书非常厚,有1000多页。里面有一些内容可能与工作无涉。仅仅要大概知道有那回事就可以了,临时不必深入学习,等须要时再进一步学习并运用。我在开发的项目中曾用到的光照、雾化等效果,都是之前先知道有这个东西。后来在项目中才逐渐学习运用的。

4)嵌入式平台上用的事实上是OpenGL ES。这里。另一本书叫《OpenGL ES 2.0 Programming Guide》,它介绍了OpenGL ES的开发,读者可认真修习。

5)在Android SDK文档中。对OpenGL API的描写叙述仅仅寥寥数语。怎么办?由于它使用了J2ME中的javax.microedition.khronos.opengles包。所以J2ME的SDK文档中对OpenGL的API有着非常具体的描写叙述。读者手头应该要有一个J2ME的文档。

6)假设想做深入开发,就不得不学习计算机图形学了。我后来买了书。可惜没时间学了。

4. unlockClients和postFrameBuffer的分析

在绘制完图后,还有两项工作须要做,一个涉及unlockClients函数,另外一个涉及postFrameBuffer函数,这两个函数分别干了什么呢?unlockClients的代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::unlockClients()

{

constLayerVector& drawingLayers(mDrawingState.layersSortedByZ);

constsize_t count = drawingLayers.size();

sp<LayerBase> const* const layers = drawingLayers.array();

for (size_t i=0 ; i<count ; ++i) {

const sp<LayerBase>& layer = layers[i];

layer->finishPageFlip();

}

}

再看Layer的finishPageFlip函数,代码例如以下所看到的:

[-->Layer.cpp]

void Layer::finishPageFlip()

{

//释放FrontBufferIndex

status_t err = lcblk->unlock( mFrontBufferIndex );

}

原来,unlockClients会释放之前占着的FrontBuffer的索引號。

以下看最后一个函数postFrameBuffer,代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::postFramebuffer()

{

if(!mInvalidRegion.isEmpty()) {

const DisplayHardware& hw(graphicPlane(0).displayHardware());

const nsecs_t now = systemTime();

mDebugInSwapBuffers = now;

//调用这个函数后。混合后的图像就会传递到屏幕中显示了

hw.flip(mInvalidRegion);

mLastSwapBufferTime = systemTime() - now;

mDebugInSwapBuffers = 0;

mInvalidRegion.clear();

}

}

flip将调用在DisplayHardware一节中提到的eglSwapBuffer函数,来完毕FrameBuffer的PageFlip操作,代码例如以下所看到的:

[-->DisplayHardware.cpp]

void DisplayHardware::flip(const Region&dirty) const

{

checkGLErrors();

EGLDisplay dpy = mDisplay;

EGLSurface surface = mSurface;

......

if(mFlags & PARTIAL_UPDATES) {

mNativeWindow->setUpdateRectangle(dirty.getBounds());

}

mPageFlipCount++;

eglSwapBuffers(dpy, surface);//PageFlipping,此后图像终于显示在屏幕上了!

}

8.5.3  Transaction的分析

Transaction是“事务”的意思。在我脑海中,关于事务的知识来自于数据库。在数据库操作中,事务意味着一次能够提交多个SQL语句,然后一个commit就可让它们集中运行。而且数据库中的事务还能够回滚。即恢复到事务提交前的状态。

SurfaceFlinger为什么须要事务呢?从上面对数据库事务的描写叙述来看,是不是意味着一次运行多个请求呢?如直接盯着SF的源代码来分析,可能不太easy搞清楚事务的前因后果,我想还是用老办法。从一个样例入手吧。

在WindowManagerService.java中,有一个函数之前分析过,如今再看看,代码例如以下所看到的:

[-->WindowManagerService.java::WinState]

Surface createSurfaceLocked() {

Surface.openTransaction(); //開始一次transaction

try {

try {

mSurfaceX = mFrame.left + mXOffset;

mSurfaceY = mFrame.top + mYOffset;

//设置Surface的位置

mSurface.setPosition(mSurfaceX, mSurfaceY);

......

}

}finally {

Surface.closeTransaction(); //关闭这次事务

}

这个样例非常好地展示了事务的调用流程,它会依次调用:

·  openTransaction

·  setPosition

·  closeTransaction

以下就来分析这几个函数的调用。

1. openTransaction的分析

看JNI相应的函数,代码例如以下所看到的:

[-->android_View_Surface.cpp]

static void Surface_openTransaction(JNIEnv* env,jobject clazz)

{

//调用SurfaceComposerClient的openGlobalTransaction函数

SurfaceComposerClient::openGlobalTransaction();

}

以下转到SurfaceComposerClient,代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

voidSurfaceComposerClient::openGlobalTransaction()

{

Mutex::Autolock _l(gLock);

......

constsize_t N = gActiveConnections.size();

for(size_t i=0; i<N; i++) {

sp<SurfaceComposerClient>client(gActiveConnections.valueAt(i).promote());

//gOpenTransactions存储当前提交事务请求的Client

if(client != 0 && gOpenTransactions.indexOf(client) < 0) {

//Client是保存在全局变量gActiveConnections中的SurfaceComposerClient

//对象。调用它的openTransaction。

if (client->openTransaction() == NO_ERROR) {

if (gOpenTransactions.add(client) < 0) {

client->closeTransaction();

}

}

......

}

}

}

上面是一个静态函数,内部调用了各个SurfaceComposerClient对象的openTranscation,代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

status_tSurfaceComposerClient::openTransaction()

{

if(mStatus != NO_ERROR)

return mStatus;

Mutex::Autolock _l(mLock);

mTransactionOpen++; //一个计数值,用来控制事务的提交。

if(mPrebuiltLayerState == 0) {

mPrebuiltLayerState = new layer_state_t;

}

returnNO_ERROR;

}

layer_state_t是用来保存Surface的一些信息的,比方位置、宽、高等信息。

实际上。调用的setPosition等函数,就是为了改变这个layer_state_t中的值。

2. setPosition的分析

上文说过,SFC中有一个layer_state_t对象用来保存Surface的各种信息。这里以setPosition为例,来看它的使用情况。这个函数是用来改变surface在屏幕上的位置的,代码例如以下所看到的:

[-->android_View_Surface.cpp]

static void Surface_setPosition(JNIEnv* env,jobject clazz, jint x, jint y)

{

constsp<SurfaceControl>& surface(getSurfaceControl(env, clazz));

if(surface == 0) return;

status_t err = surface->setPosition(x, y);

}

[-->Surface.cpp]

status_t SurfaceControl::setPosition(int32_t x,int32_t y) {

constsp<SurfaceComposerClient>& client(mClient);

status_t err = validate();

if (err < 0) return err;

//调用SurfaceComposerClient的setPosition函数

returnclient->setPosition(mToken, x, y);

}

[-->SurfaceComposerClient.cpp]

status_tSurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y)

{

layer_state_t* s = _lockLayerState(id); //找到相应的layer_state_t

if(!s) return BAD_INDEX;

s->what |= ISurfaceComposer::ePositionChanged;

s->x = x;

s->y = y;  //上面几句改动了这块layer的參数

_unlockLayerState(); //该函数将unlock一个同步对象,其它没有做什么工作

returnNO_ERROR;

}

setPosition就是改动了layer_state_t中的一些參数,那么。这个状态是什么时候传递到SurfaceFlinger中的呢?

3. 分析closeTransaction

相信读者此时已明确为什么叫“事务”了。

原来。在openTransaction和closeTransaction中能够有非常多操作。然后由closeTransaction一次性地把这些改动提交到SF上。来看代码:

[-->android_View_Surface.cpp]

static void Surface_closeTransaction(JNIEnv*env, jobject clazz)

{

SurfaceComposerClient::closeGlobalTransaction();

}

[-->SurfaceComposerClient.cpp]

voidSurfaceComposerClient::closeGlobalTransaction()

{

......

const size_t N = clients.size();

sp<ISurfaceComposer>sm(getComposerService());

//①先调用SF的openGlobalTransaction

sm->openGlobalTransaction();

for (size_t i=0; i<N; i++) {

//②然后调用每一个SurfaceComposerClient对象的closeTransaction

clients[i]->closeTransaction();

}

//③最后调用SF的closeGlobalTransaction

sm->closeGlobalTransaction();

}

上面一共列出了三个函数,它们都是跨进程的调用,以下对其一一进行分析。

(1)SurfaceFlinger的openGlobalTransaction分析

这个函数事实上非常easy。略看就可以了。

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::openGlobalTransaction()

{

android_atomic_inc(&mTransactionCount);//又是一个计数控制

}

(2)SurfaceComposerClient的closeTransaction分析

代码例如以下所看到的:

[-->SurfaceComposerClient.cpp]

status_tSurfaceComposerClient::closeTransaction()

{

if(mStatus != NO_ERROR)

return mStatus;

Mutex::Autolock _l(mLock);

......

constssize_t count = mStates.size();

if (count) {

//mStates是这个SurfaceComposerClient中保存的全部layer_state_t数组。也就是

//每一个Surface一个。

然后调用跨进程的setState

mClient->setState(count, mStates.array());

mStates.clear();

}

returnNO_ERROR;

}

BClient的setState,终于会转到SF的setClientState上。代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

status_t SurfaceFlinger::setClientState(ClientIDcid, int32_t count,

const layer_state_t*states)

{

Mutex::Autolock _l(mStateLock);

uint32_t flags = 0;

cid<<= 16;

for(int i=0 ; i<count ; i++) {

const layer_state_t& s = states[i];

sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid));

if(layer != 0) {

const uint32_t what = s.what;

if (what & ePositionChanged) {

if (layer->setPosition(s.x, s.y))

//eTraversalNeeded表示须要遍历全部显示层

flags |= eTraversalNeeded;

}

....

if(flags) {

setTransactionFlags(flags);//这里将会触发threadLoop的事件。

}

returnNO_ERROR;

}

[-->SurfaceFlinger.cpp]

uint32_tSurfaceFlinger::setTransactionFlags(uint32_t flags, nsecs_t delay)

{

uint32_t old = android_atomic_or(flags, &mTransactionFlags);

if((old & flags)==0) {

if(delay > 0) {

signalDelayedEvent(delay);

}else {

signalEvent();  //设置完mTransactionFlags后,触发事件。

}

}

returnold;

}

(3)SurfaceFlinger的closeGlobalTransaction分析

来看代码:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::closeGlobalTransaction()

{

if (android_atomic_dec(&mTransactionCount) ==1) {

//注意以下语句的运行条件,当mTransactionCount变为零时才运行,这意味着

//openGlobalTransaction两次的话。仅仅有最后一个closeGlobalTransaction调用

//才会真正地提交事务

signalEvent();

Mutex::Autolock _l(mStateLock);

//假设这次事务涉及尺寸调整,则须要等一段时间

while (mResizeTransationPending) {

status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));

if (CC_UNLIKELY(err != NO_ERROR)) {

mResizeTransationPending = false;

break;

}

}

}

}

关于事务的目的。相信读者已经比較清楚了:

·  就是将一些控制操作(比如setPosition)的改动结果,一次性地传递给SF进行处理。

那么,哪些操作须要通过事务来传递呢?通过查看Surface.h能够知道,以下这些操作须要通过事务来传递(这里仅仅列出了几个经经常使用的函数):setPosition、setAlpha、show/hide、setSize、setFlag等。

由于这些改动不像重绘那么简单,有时它会涉及其它的显示层。比如在显示层A的位置调整后。之前被A遮住的显示层B,如今可能变得可见了。对于这样的情况,所提交的事务会设置eTraversalNeeded标志,这个标志表示要遍历全部显示层进行处理。

关于这一点,来看工作线程中的事务处理。

4. 工作线程中的事务处理

还是从代码入手分析。例如以下所看到的:

[-->SurfaceFlinger.cpp]

bool SurfaceFlinger::threadLoop()

{

waitForEvent();

if(LIKELY(mTransactionCount == 0)) {

const uint32_t mask = eTransactionNeeded | eTraversalNeeded;

uint32_ttransactionFlags = getTransactionFlags(mask);

if(LIKELY(transactionFlags)) {

handleTransaction(transactionFlags);

}

}

...

}

getTransactionFlags函数的实现蛮有意思,最好还是看看其代码,例如以下所看到的:

[-->SurfaceFlinger.cpp]

uint32_t SurfaceFlinger::getTransactionFlags(uint32_tflags)

{

//先通过原子操作去掉mTransactionFlags中相应的位。

//而后原子操作返回的旧值和flags进行与操作

return android_atomic_and(~flags,&mTransactionFlags) & flags;

}

getTransactionFlags所做的工作不仅仅是get那么简单,它还设置了mTransactionFlags,从这个角度来看,getTransactionFlags这个名字有点名不副实。

接着来看最重要的handleTransaction函数。代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::handleTransaction(uint32_ttransactionFlags)

{

Vector< sp<LayerBase> > ditchedLayers;

{

Mutex::Autolock _l(mStateLock);

//调用handleTransactionLocked函数处理

handleTransactionLocked(transactionFlags, ditchedLayers);

}

constsize_t count = ditchedLayers.size();

for(size_t i=0 ; i<count ; i++) {

if(ditchedLayers[i] != 0) {

//ditch是丢弃的意思,有些显示层可能被hide了。所以这里做些收尾的工作

ditchedLayers[i]->ditch();

}

}

}

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::handleTransactionLocked(

uint32_t transactionFlags, Vector< sp<LayerBase> >&ditchedLayers)

{

//这里使用了mCurrentState,它的layersSortedByZ数组存储了SF中全部的显示层

constLayerVector& currentLayers(mCurrentState.layersSortedByZ);

constsize_t count = currentLayers.size();

constbool layersNeedTransaction = transactionFlags & eTraversalNeeded;

//假设须要遍历全部显示的话。

if(layersNeedTransaction) {

for (size_t i=0 ; i<count ; i++) {

const sp<LayerBase>& layer = currentLayers[i];

uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);

if (!trFlags) continue;

//调用各个显示层的doTransaction函数。

constuint32_t flags = layer->doTransaction(0);

if (flags & Layer::eVisibleRegion)

mVisibleRegionsDirty = true;

}

}

if(transactionFlags & eTransactionNeeded) {

if(mCurrentState.orientation != mDrawingState.orientation) {

//横竖屏假设发生切换。须要相应变换设置。

const int dpy = 0;

const int orientation = mCurrentState.orientation;

const uint32_t type = mCurrentState.orientationType;

GraphicPlane& plane(graphicPlane(dpy));

plane.setOrientation(orientation);

......

}

/*

mLayersRemoved变量在显示层被移除的时候设置。比如removeLayer函数。这些函数

也会触发handleTranscation函数的运行

*/

if(mLayersRemoved) {

mLayersRemoved = false;

mVisibleRegionsDirty = true;

const LayerVector& previousLayers(mDrawingState.layersSortedByZ);

const size_t count = previousLayers.size();

for (size_t i=0 ; i<count ; i++) {

const sp<LayerBase>& layer(previousLayers[i]);

if (currentLayers.indexOf( layer ) < 0) {

ditchedLayers.add(layer);

mDirtyRegionRemovedLayer.orSelf(layer->visibleRegionScreen);

}

}

}

free_resources_l();

}

//提交事务处理。有必要进去看看。

commitTransaction();

}

每一个显示层对事务的具体处理,都在它们的doTranscation函数中,读者若有兴趣,可进去看看。须要说明的是。每一个显示层内部也有一个状态变量,doTransaction会更新这些状态变量。

回到上面的函数。最后它将调用commitTransaction提交事务,代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

void SurfaceFlinger::commitTransaction()

{

//mDrawingState将使用更新后的mCurrentState

mDrawingState = mCurrentState;

mResizeTransationPending = false;

//触发一个条件变量。这样等待在closeGlobalTransaction函数中的线程能够放心地返回了。

mTransactionCV.broadcast();

}

8.5.4  SurfaceFlinger的总结

通过前面的分析,使我们感受了SurfaceFlinger的风採。从总体上看,SurfaceFlinger不如AudioFlinger复杂,它的工作集中在工作线程中,以下用图8-23来总线一下SF工作线程:

图8-23  SF工作线程的流程总结

8.6拓展思考

本章的拓展思考分三个部分:

·  介绍SharedBufferServer和SharedBufferClient的工作流程。

·  关于ViewRoot一些问题的总结。

·  LayerBuffer的工作原理分析。

8.6.1  Surface系统的CB对象分析

依据前文分析可知,Surface系统中的CB,事实上是指SharedBuffer家族,它们是Surface系统中对生产者和消费者进行步调控制的中枢机构。先通过图8-24来观察总体的工作流程是如何的。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-24  SharedBuffer家族使用流程

为书写方便起见,我们简称:

·  SharedBufferServer为SBS。

·  SharedBufferClient为SBC。

·  SharedBufferStack为SBT。

当中SBC和SBS都是建立在同一个SBT上的,所以应先看SBT,以下代码列出了当中几个与读写控制有关的成员变量:

[-->SharedBufferStack.h]

class SharedBufferStack{

......

/*

尽管PageFlipping使用Front和Back两个Buffer就能够了,可是SBT的结构和相关算法

是支持多个缓冲的。

另外,缓冲是依照块来获取的,也就是一次获得一块缓冲,每块缓冲用

一个编号表示(这一点在之前的分析已经介绍过了)。

*/

int32_t head;

int32_tavailable; //当前可用的空暇缓冲个数

int32_t queued;    //SBC投递的脏缓冲个数

int32_tinUse;  //SBS当前正在使用的缓冲编号

......//上面这几个參数联合SBC中的tail。我称之为控制參数。

}

SBT创建好后。以下就是SBS和SBC的创建了,它们会做什么特殊工作吗?

1. SBS和SBC的创建

以下分别看SBS和SBC的创建。代码例如以下所看到的:

[-->SharedBufferStack.cpp]

SharedBufferServer::SharedBufferServer(SharedClient*sharedClient,

int surface, int num, int32_t identity)

:SharedBufferBase(sharedClient, surface, num, identity)

{

mSharedStack->init(identity);//这个函数将设置inUse为-1

//以下设置SBT中的參数,我们关注前三个

mSharedStack->head = num-1;

mSharedStack->available = num;

mSharedStack->queued = 0;

//设置完后,head=2-1=1,available=2,queued=0,inUse=-1

mSharedStack->reallocMask = 0;

memset(mSharedStack->dirtyRegion, 0,sizeof(mSharedStack->dirtyRegion));

}

再看SBC的创建,代码例如以下所看到的:

[-->SharedBufferStack.cpp]

SharedBufferClient::SharedBufferClient(SharedClient*sharedClient,

int surface, int num, int32_t identity)

:SharedBufferBase(sharedClient, surface, num, identity), tail(0)

{

tail =computeTail(); //tail是SBC定义的变量。注意它不是SBT定义的。

}

看computeTail函数的代码:

[-->SharedBufferStack.cpp]

int32_t SharedBufferClient::computeTail() const

{

SharedBufferStack& stack( *mSharedStack );

int32_t newTail;

int32_t avail;

int32_t head;

do {

avail = stack.available; //available=2,head=1

head = stack.head;

}while (stack.available != avail);

newTail = head - avail + 1;//newTail=1-2+1=0

if(newTail < 0) {

newTail += mNumBuffers;

} elseif (newTail >= mNumBuffers) {

newTail -= mNumBuffers;

}

return newTail;//计算得到newTail=0

}

来看在SBC和SBS创建后,控制參数的变化,如图8-25所看到的:

图8-25  SBC/SBS创建后的示意图

2. SBC端流程的分析

以下看SBC端的工作流程。

(1)dequeue分析

先看SBC的dequeue函数:

[-->SharedBufferStack.cpp]

ssize_t SharedBufferClient::dequeue()

{

SharedBufferStack& stack( *mSharedStack );

......

//DequeueCondition函数对象

DequeueCondition condition(this);

status_t err = waitForCondition(condition);

//成功以后,available减1,表示当前可用的空暇buffer仅仅有1个

if (android_atomic_dec(&stack.available) == 0) {

......

}

int dequeued = tail; //tail值为0。所以dequeued的值为0。

//tail加1。假设超过2,则又一次置为0,这表明tail的值在0,1间循环。

tail =((tail+1 >= mNumBuffers) ? 0 : tail+1);

......

//返回的这个dequeued值为零,也就是tail加1操作前的旧值。这一点请读者务必注意。

returndequeued;

}

当中DequeueCondition的操作函数非常easy,代码例如以下所看到的:

bool SharedBufferClient::DequeueCondition::operator()(){

returnstack.available > 0;//仅仅要available大于0就算满足条件。第一次进来肯定满足

}

用图8-26来表示dequeue的结果:

图8-26  dequeue结果图

注意。在上图中。0号缓冲用虚线表示,SBC的dequeue函数的返回值用dequeued表示。它指向这个0号缓冲。

正如代码中凝视的那样。由于dequeued的值用的是tail的旧值,而tail是SBC定义的变量,不是SBT定义的变量,所以tail在SBS端是不可见的。这就带来了一个潜在危急,即0号缓冲不能保证当前是真正空暇的,由于SBS可能正在用它,怎么办?试看以下的lock。

(2)lock的分析

lock使用了LockCondition,当中传入的參数buf的值为0。也就是上图中的dequeue的值。代码例如以下所看到的:

[-->SharedBufferStack.cpp]

status_t SharedBufferClient::lock(int buf)

{

LockCondition condition(this, buf);

status_terr = waitForCondition(condition);

returnerr;

}

看LockCondition的()函数:

boolSharedBufferClient::LockCondition::operator()() {

/*

这个条件事实上就是推断编号为buf的Buffer是不是被使用了。

buf值为0,head值为1。queued为0,inUse为-1

*/

return(buf != stack.head ||

(stack.queued > 0 && stack.inUse!= buf));

}

如今能够知道为什么SBC须要调用dequeue和lock函数了吗?原来:

·  dequeue仅仅是依据本地变量tail计算一个本次应当使用的Buffer编号,事实上也就是在0,1之间循环。

上次用0号缓冲。那么这次就用1号缓冲。

·  lock函数要确保这个编号的Buffer没有被SF当做FrontBuffer使用。

(3)queue的分析

Activity端在绘制完UI后,将把BackBuffer投递出去以显示。接着上面的流程,这个BackBuffer的编号是0。待Activity投递完后。才会调用signal函数触发SF消费,所以在此之前格局不会发生变化。试看投递用的queue函数。注意传入的buf參数为0。代码例如以下所看到的:

[-->SharedBufferStack.cpp]

status_t SharedBufferClient::queue(int buf)

{

QueueUpdate update(this);

status_t err = updateCondition( update );

......

returnerr;

}

//直接看这个QueueUpdate函数对象

ssize_tSharedBufferClient::QueueUpdate::operator()() {

android_atomic_inc(&stack.queued);//queued增加1。如今该值由零变为1

returnNO_ERROR;

}

至此,SBC端走完一个流程了。结果是什么?如图8-27所看到的:

图8-27  queue结果图

0号缓冲被移到queue的区域了,可眼下还没有变量指向它。

假设SBC端此后没有绘制UI的需求,那么它就会沉默一段时间。

3. SBS端的分析

SBS的第一个函数是retireAndLock。它使用了RetireUpdate函数对象,代码例如以下所看到的:

[-->SharedBufferStack.cpp]

ssize_t SharedBufferServer::retireAndLock()

{

RetireUpdate update(this, mNumBuffers);

ssize_t buf = updateCondition( update );

returnbuf;

}

这个RetireUpdate对象的代码例如以下所看到的:

ssize_tSharedBufferServer::RetireUpdate::operator()() {

//先取得head值,为1

int32_t head = stack.head;

//inUse被设置为1。表明要使用1吗?眼下的脏缓冲应该是0才对

android_atomic_write(head, &stack.inUse);

int32_tqueued;

do {

queued = stack.queued; //queued眼下为1

if(queued == 0) {

return NOT_ENOUGH_DATA;

}

//以下这个原子操作使得stack.queued减1.

}while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));

//while循环退出后,queued减1,又变为0。

//head值也在0,1间循环。如今head值变为0了

head =((head+1 >= numBuffers) ? 0 : head+1);

//inUse被设置为0

android_atomic_write(head, &stack.inUse);

// head值被设为0

android_atomic_write(head, &stack.head);

// available加1,变成2.

android_atomic_inc(&stack.available);

returnhead;//返回0

}

retireAndLock的结果是什么呢?看看图8-28就知道了。

图8-28  retireAndLock结果图

注意上面的available区域。1号缓冲右边的0号缓冲是用虚线表示的,这表示该0号缓冲实际上并不存在于available区域,但available的个数却变成2了。这样不会出错吗?当然不会。由于SBC的lock函数要确保这个缓冲没有被SBS使用。

我们来看SBS端最后一个函数,它调用了SBS的unlock,这个unlock使用了UnlockUpdate函数对象。就直接了解它好了,代码例如以下所看到的:

[-->SharedBufferStack.cpp]

ssize_tSharedBufferServer::UnlockUpdate::operator()() {

......

android_atomic_write(-1, &stack.inUse);//inUse被设置为-1

returnNO_ERROR;

}

unlock后终于的结果是什么呢?如图8-29所看到的:

图8-29  unlock结果图

比較一下图8-29和图8-25,可能会发现两图中tail和head刚好反了,这就是PageFlip。

另外。上面的函数大量使用了原子操作。原子操作的目的就是为了避免锁的使用。

值得指出的是。updateConditon函数和waitForCondition函数都使用了Mutex。也就是说,上面这些函数对象又都是在Mutex锁的保护下运行的,为什么会这样呢?先来看一段代码:

像以下这样的代码,假设有锁控制的话根本用不着一个while循环,由于有锁的保护。没有其它线程

能够改动stack.queued的值,所以用while来循环推断android_atomic_cmpxchg没有什么意义。

int32_tqueued;

do {

queued = stack.queued;

if(queued == 0) {

return NOT_ENOUGH_DATA;

}

}while (android_atomic_cmpxchg(queued, queued-1, &stack.queued));

对于上面这个问题。我眼下还不知道答案,但对其也进行了改动,把函数对象放在锁外运行,结果在真机上运行没有出现不论什么异常现象。也许Google或哪位读者能给这个问题一个较好的解释。

为什么我对生产/消费的同步控制如此感兴趣呢?这和自己工作的经历有些关系。

由于之前曾做过一个单写多读的跨进程缓冲类,也就是一个生产者。多个消费者。为了保证正确性和一定的效率,我们在算法上曾做了非常多改进,但还是大量使用了锁。所以我非常好奇Google是怎么做到的,这也体现了一个高手的内功修养。要是由读者自己来实现,结果会如何呢?

8.6.2  ViewRoot的你问我答

ViewRoot是Surfac系统甚至UI系统中一个非常关键的类,以下把网上一些关于ViewRoot的问题做个总结,希望这样能帮助读者对ViewRoot有更加清楚的认识。

·  ViewRoot和View类的关系是什么?

ViewRoot是View视图体系的根。每一个Window(注意是Window。比方PhoneWindow)有一个ViewRoot。它的作用是处理layout和View视图体系的绘制。那么视图体系又是什么呢?它包括Views和ViewGroups。也就是SDK中能看到的View类都属于视图体系。依据前面的分析可知,这些View是须要通过draw画出来的。而ViewRoot就是用来draw它们的,ViewRoot本身没有draw/onDraw函数。

·   ViewRoot和它所控制的View及其子View使用同一个Canvas吗?

这个问题的答案就非常easy了,我们在ViewRoot的performTraversals中见过。

ViewRoot提供Canvas给它所控制的View。所以它们使用同一个Canvas。但Canvas使用的内存却不是固定的,而是通过Surface的lockCanvas得到的。

·  View、Surface和Canvas之间的关系是如何的?我觉得。每一个view将和一个canvas,以及一个surface绑定到一起(这里的“我”表示提问人)。

这个问题的答案也非常easy。一个Window将和一个Surface绑定在一起。绘制前ViewRoot会从Surface中lock出一个Canvas。

·  Canvas有一个bitmap,那么绘制UI时,数据是画在Canvas的这个bitmap中吗?

答案是肯定的。bitmap实际上包括了一块内存。绘制的数据终于都在这块内存上。

·   同一个ViewRoot下。不同类型的View(不同类型指不同的UI单元,比如button、文本框等)使用同一个Surface吗?

是的。可是SurfaceView要除外。

由于SurfaceView的绘制一般在单独的线程上。而且由应用层主动调用lockCanvas、draw和unlockCanvasAndPost来完毕绘制流程。

应用层相当于抛开了ViewRoot的控制,直接和屏幕打交道,这在camera、video方面用得最多。

8.6.3  LayerBuffer的分析

前面介绍了Normal属性显示层中的第一类Layer。这里将介绍当中的第二类LayerBuffer。

LayerBuffer会在视频播放和摄像机预览等场景中用到,就以Camera的preView(预览)为例。来分析LayerBuffer的工作原理。

1. LayerBuffer的创建

先看LayerBuffer的创建,它通过SF的createPushBuffersSurfaceLocked得到,代码例如以下所看到的:

[-->SurfaceFlinger.cpp]

sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked(

const sp<Client>& client, DisplayID display,

int32_t id, uint32_t w, uint32_t h, uint32_t flags)

{

sp<LayerBuffer> layer = new LayerBuffer(this, display, client,id);

layer->initStates(w, h, flags);

addLayer_l(layer);

returnlayer;

}

LayerBuffer的派生关系,如图8-30所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-30  LayerBuffer的派生关系示意图

从上图中能够发现:

·  LayerBuffer定义了一个内部类Source类,它有两个派生类BufferSource和OverlaySource。依据它们的名字。能够推測到Source代表数据的提供者。

·  LayerBuffer中的mSurface其真实类型是SurfaceLayerBuffer。

LayerBuffer创建好了。只是该怎么用呢?和它相关的调用流程是如何的呢?以下来分析Camera。

2. Camera preView的分析

Camera是一个单独的Service,全称是CameraService,先看CameraService的registerPreviewBuffers函数。

这个函数会做什么呢?代码例如以下所看到的:

[-->CameraService.cpp]

status_tCameraService::Client::registerPreviewBuffers()

{

int w, h;

CameraParameters params(mHardware->getParameters());

params.getPreviewSize(&w, &h);

/*

①mHardware代表Camera设备的HAL对象。本书讨论CameraHardwareStub设备。它事实上是

一个虚拟的设备。只是其代码却具有參考价值。

BufferHeap定义为ISurface的内部类,事实上就是对IMemoryHeap的封装

*/

ISurface::BufferHeapbuffers(w, h, w, h,

HAL_PIXEL_FORMAT_YCrCb_420_SP,

mOrientation,

0,

mHardware->getPreviewHeap());

//②调用SurfaceLayerBuffer的registerBuffers函数。

status_t ret = mSurface->registerBuffers(buffers);

returnret;

}

上面代码中列出了两个关键点,逐一来分析它们。

(1)创建BufferHeap

BufferHeap是ISurface定义的一个内部类。它的声明例如以下所看到的:

[-->ISurface.h]

classBufferHeap {

public:

......

//使用这个构造函数

BufferHeap(uint32_t w, uint32_t h,

int32_t hor_stride, int32_t ver_stride,

PixelFormat format, const sp<IMemoryHeap>& heap);

......

~BufferHeap();

uint32_t w;

uint32_t h;

int32_t hor_stride;

int32_t ver_stride;

PixelFormat format;

uint32_t transform;

uint32_t flags;

sp<IMemoryHeap> heap; //heap指向真实的存储对象

};

从上面代码中可发现,BufferHeap基本上就是封装了一个IMemoryHeap对象,依据我们对IMemoryHeap的了解,它应该包括了真实的存储对象,这个值由CameraHardwareStub对象的getPreviewHeap得到。这个函数的代码例如以下所看到的:

[-->CameraHardwareStub.cpp]

sp<IMemoryHeap>CameraHardwareStub::getPreviewHeap() const

{

returnmPreviewHeap;//返回一个成员变量,它又是在哪创建的呢?

}

//上面的mPreivewHeap对象由initHeapLocked函数创建。该函数在HAL对象创建的时候被调用

void CameraHardwareStub::initHeapLocked()

{

......

/*

创建一个MemoryHeapBase对象,大小是mPreviewFrameSize * kBufferCount,当中

kBufferCount为4。注意这是一段连续的缓冲。

*/

mPreviewHeap= new MemoryHeapBase(mPreviewFrameSize * kBufferCount);

//mBuffer为MemoryBase数组。元素为4

for (inti = 0; i < kBufferCount; i++) {

mBuffers[i] = new MemoryBase(mPreviewHeap,

i * mPreviewFrameSize, mPreviewFrameSize);

}

}

从上面这段代码中能够发现,CameraHardwareStub对象创建的用于preView的内存结构是按图8-31所看到的的方式来组织的:

图8-31  CameraHardwareStub用于preView的内存结构图

当中:

·  BufferHeap的heap变量指向一块MemoryHeap,这就是mPreviewHeap。

·  在这块MemoryHeap上构建了4个MemoryBase。

(2)registerBuffers的分析

BufferHeap准备好后。要调用ISurface的registerBuffers函数,ISurface在SF端的真实类型是SurfaceLayerBuffer,所以要直接看它的实现,代码例如以下所看到的:

[-->LayerBuffer.cpp]

status_t LayerBuffer::SurfaceLayerBuffer::registerBuffers(

const ISurface::BufferHeap& buffers)

{

sp<LayerBuffer> owner(getOwner());

if (owner != 0)

//调用外部类对象的registerBuffers,所以SurfaceLayerBuffer也是一个Proxy哦。

return owner->registerBuffers(buffers);

returnNO_INIT;

}

//外部类是LayerBuffer。调用它的registerBuffers函数

status_t LayerBuffer::registerBuffers(constISurface::BufferHeap& buffers)

{

Mutex::Autolock _l(mLock);

//创建数据的来源BufferSource,注意我们事实上把MemoryHeap设置上去了

sp<BufferSource> source = new BufferSource(*this, buffers);

status_t result = source->getStatus();

if(result == NO_ERROR) {

mSource = source;//保存这个数据源为mSource。

}

returnresult;

}

BufferSource,曾在图8-30中见识过,它内部有一个成员变量mBufferHeap指向传入的buffers參数。所以registerBuffers过后,就得到了图8-32:

图8-32  registerBuffers的结果示意图

请注意上图的箭头指向。不论中间有多少层封装,终于的数据存储区域还是mPreivewHeap。

2.数据的传输

至此,Buffer在SF和Camera两端都准备好了,那么数据是怎么从Camera传递到SF的呢?先来看数据源是怎么做的。

(1)传输数据的分析

CameraHardwareStub有一个preview线程,这个线程会做什么呢?代码例如以下所看到的:

[-->CameraHardwareStub.cpp]

//preview线程从Thread类派生。以下这个函数在threadLoop中循环调用

int CameraHardwareStub::previewThread()

{

mLock.lock();

//每次进来mCurrentPreviewFrame都会加1

ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize;

sp<MemoryHeapBase> heap = mPreviewHeap;

FakeCamera* fakeCamera = mFakeCamera;//虚拟的摄像机设备

//从mBuffers中取一块内存,用于接收来自硬件的数据

sp<MemoryBase>buffer = mBuffers[mCurrentPreviewFrame];

mLock.unlock();

if(buffer != 0) {

intdelay = (int)(1000000.0f / float(previewFrameRate));

void *base = heap->base();//base是mPreviewHeap的起始位置

//以下这个frame代表buffer在mPreviewHeap中的起始位置,还记得图8-31吗?

//四块MemoryBase的起始位置由以下这个代码计算得来

uint8_t *frame = ((uint8_t *)base) + offset;

//取出一帧数据,放到相应的MemoryBase中

fakeCamera->getNextFrameAsYuv422(frame);

//①把含有帧数据的buffer传递到上层

if(mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)

mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, mCallbackCookie);

//mCurrentPreviewFrame 递增,在0到3之间循环

mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount;

usleep(delay);//模拟真实硬件的延时

}

returnNO_ERROR;

}

读者是否明确Camera preview的工作原理了?就是从四块内存中取一块出来接收数据。然后再把这块内存传递到上层去处理。从缓冲使用的角度来看,mBuffers数组构成了一个成员个数为四的缓冲队列。preview通过mData这个回调函数,把数据传递到上层,而CameraService实现了mData这个回调函数。这个回调函数终于会调用handlePreviewData,直接看handlePreviewData就可以。代码例如以下所看到的:

[-->CameraService.cpp]

voidCameraService::Client::handlePreviewData(const sp<IMemory>& mem)

{

ssize_t offset;

size_t size;

//注意传入的mem參数,它实际上是Camera HAL创建的mBuffers数组中的一个

//offset返回的是这个数组在mPreviewHeap中的偏移量

sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);

if (!mUseOverlay)

{

Mutex::Autolock surfaceLock(mSurfaceLock);

if(mSurface != NULL) {

//调用ISurface的postBuffer,注意我们传入的參数是offset。

mSurface->postBuffer(offset);

}

}

......

}

上面的代码是什么意思?我们究竟给ISurface传什么了?答案非常明显:

·  handlePreviewData就是传递了一个偏移量,这个偏移量是mBuffers数组成员的首地址。可用图8-33来表示:

图8-33  handlePreviewData示意图

有了图8-33,读者明确数据传递的工作原理了吗?

以下看SurfaceLayerBuffer的postBuffer函数。只是它仅仅是一个小小的代理。真正的工作由外部类LayerBuffer完毕,直接看它好了,代码例如以下所看到的:

[-->LayerBuffer.cpp]

void LayerBuffer::postBuffer(ssize_t offset)

{

sp<Source> source(getSource());//getSource返回mSource,为BufferSource类型

if(source != 0)

source->postBuffer(offset);//调用BufferSource的postBuffer函数。

}

[-->LayerBuffer.cpp]

voidLayerBuffer::BufferSource::postBuffer(ssize_t offset)

{

ISurface::BufferHeap buffers;

{

Mutex::Autolock _l(mBufferSourceLock);

buffers = mBufferHeap;//还记得图8-32吗?

if(buffers.heap != 0) {

//BufferHeap的heap变量指向MemoryHeap,以下取出它的大小

const size_t memorySize = buffers.heap->getSize();

//做一下检查,推断这个offset是不是有问题

if ((size_t(offset) + mBufferSize) > memorySize) {

LOGE("LayerBuffer::BufferSource::postBuffer() "

"invalid buffer(offset=%d, size=%d, heap-size=%d",

int(offset),int(mBufferSize), int(memorySize));

return;

}

}

}

sp<Buffer> buffer;

if (buffers.heap != 0) {

//创建一个LayerBuffer::Buffer

buffer = new LayerBuffer::Buffer(buffers, offset, mBufferSize);

if(buffer->getStatus() != NO_ERROR)

buffer.clear();

setBuffer(buffer);//setBuffer?我们要看看

//mLayer就是外部类LayerBuffer。调用它的invalidate函数将触发SF的重绘

mLayer.invalidate();

}

}

void LayerBuffer::BufferSource::setBuffer(

const sp<LayerBuffer::Buffer>& buffer)

{

//setBuffer函数就是简单地将new出来的Buffer设置给成员变量mBuffer,这么做会有问题吗?Mutex::Autolock_l(mBufferSourceLock);

mBuffer = buffer; //将新的buffer设置为mBuffer,mBuffer原来指向的那个被delete

}

从数据生产者角度看,postBuffer函数将不断地new一个Buffer出来,然后将它赋值给成员变量mBuffer,也就是说。mBuffer会不断变化。如今从缓冲的角度来思考一下这样的情况的结果:

·  数据生产者有一个含四个成员的缓冲队列,也就是mBuffers数组。

·  而数据消费者仅仅有一个mBuffer。

这样的情况会有什么后果呢?请记住这个问题,我们到最后再来揭示。以下先看mBuffer的类型Buffer是什么。

(2)数据使用的分析

Buffer被定义成LayerBuffer的内部类。代码例如以下所看到的:

[-->LayerBuffer.cpp]

LayerBuffer::Buffer::Buffer(constISurface::BufferHeap& buffers,

ssize_t offset, size_t bufferSize)

:mBufferHeap(buffers), mSupportsCopybit(false)

{

//注意,这个src被定义为引用。所以改动src的信息相当于改动mNativeBuffer的信息

NativeBuffer& src(mNativeBuffer);

src.crop.l = 0;

src.crop.t = 0;

src.crop.r = buffers.w;

src.crop.b = buffers.h;

src.img.w       =buffers.hor_stride ?

: buffers.w;

src.img.h       =buffers.ver_stride ?: buffers.h;

src.img.format  = buffers.format;

//这个base将指向相应的内存起始地址

src.img.base    =(void*)(intptr_t(buffers.heap->base()) + offset);

src.img.handle  = 0;

gralloc_module_tconst * module = LayerBuffer::getGrallocModule();

//做一些处理,有兴趣的读者能够去看看。

if(module && module->perform) {

int err = module->perform(module,

GRALLOC_MODULE_PERFORM_CREATE_HANDLE_FROM_BUFFER,

buffers.heap->heapID(), bufferSize,

offset, buffers.heap->base(),

&src.img.handle);

mSupportsCopybit = (err == NO_ERROR);

}

}

上面是Buffer的定义。当中最重要的就是这个mNativeBuffer了。它实际上保存了mBuffers数组成员的首地址。

以下看画图函数。也就是LayerBuffer的onDraw函数。这个函数由SF的工作线程调用。代码例如以下所看到的:

[-->LayerBuffer.cpp]

void LayerBuffer::onDraw(const Region& clip)const

{

sp<Source> source(getSource());

if(LIKELY(source != 0)) {

source->onDraw(clip);//source实际类型是BufferSource,我们去看看。

} else{

clearWithOpenGL(clip);

}

}

void LayerBuffer::BufferSource::onDraw(constRegion& clip) const

{

sp<Buffer> ourBuffer(getBuffer());

......//使用这个Buffer,注意使用的时候没有锁控制

mLayer.drawWithOpenGL(clip, mTexture);//生成一个贴图,然后绘制它

}

当中getBuffer函数返回mBuffer,代码例如以下所看到的:

sp<LayerBuffer::Buffer>LayerBuffer::BufferSource::getBuffer() const

{

Mutex::Autolock_l(mBufferSourceLock);

returnmBuffer;

}

从上面的代码中能发现,mBuffer的使用并没有锁的控制,这会导致什么问题发生呢?请再次回到前面曾强调要记住的那个问题。此时生产者的队列有四个元素。而消费者的队列仅仅有一个元素。它可用图8-34来表示:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" />

图8-34  数据传递的问题示意图

从上图能够知道:

·  使用者使用mBuffer,这是在SF的工作线程中做到的。假设mBuffer实际指向的内存为mBuffers[0]。

·  数据生产者循环更新mBuffers数组各个成员的数据内容。这是在另外一个线程中完毕的。由于这两个线程之间没有锁同步,这就造成了当使用者还在使用mBuffers[0]时,生产者又更新了mBuffers[0]。这会在屏幕上产生混杂的图像。

经过实际測试得知,假设给数据使用端加上一定延时,屏幕就会出现不连续的画面,即前一帧和后一帧的数据混杂在一起输出。

从代码的分析来看,这样的方式确实有问题。我在真实设备上測试的结果,也在一定程度上验证了这一点。通过改动LayerBuffer来解决这问题的难度比較大。是否可在读写具体缓存时加上同步控制呢(比如使用mBuffers[0]的时候调用一下lock。用完后调用unlock)?这样就不用改动LayerBuffer了。读者可再深入研究这个问题。

8.7  本章小结

本章可能是全书难度最大的一章了。在这一章的解说中。我们把打通任督二脉做为破解Surface系统的突破口:

·  应用程序和Surface的关系,这是任脉。

·  Surface和SurfaceFlinger的关系,这是督脉。

当中,打通任脉的过程是比較曲折的。从应用程序的Activity開始,一路追踪到ViewRoot、WindowManagerService。

任脉被打通后,还仅仅是攻克了Java层的问题。而督脉则集中在Native层。在必杀技aidl工具的帮助下,我们首先成功找到了Surface乾坤大挪移的踪迹。

此后在精简流程方法的帮助下,乘胜追击。对Surface以及SurfaceFlinger进行了深入分析。我希望读者在阅读过程中,也要把握流程,这样就不至于迷失在代码中了。

在拓展部分,对Surface系统中CB对象的工作流程、ViewRoot的一些问题、以及LayerBuffer进行了较为具体的介绍。


[①]说实话。笔者刚接触Android UI的时候也有点分不清楚View和Window的差别。

② 最近网络中流行的一种文体,其特点就是会用非常多感叹号。

时间: 2024-10-07 04:17:34

[深入理解Android卷一全文-第八章]深入理解Surface系统的相关文章

[深入理解Android卷一 全文-第二章]深入理解JNI

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. (出版社排版好的PDF版正在向出版社申请,到时候会通过CSDN下载资源发布) 第2章  深入理解JNI 本章主要内容 ·  通过一个实例,介绍JNI技术和在使用中应注意的问题. 本章涉及的源代码文件名及位置 下面是本章分析的源码文件名及其位置. ·  MediaScanner.java framework/b

[深入理解Android卷一全文-第十章]深入理解MediaScanner

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在OSC博客中全文转发这两本书的全部内容. 第10章 深入理解MediaScanner 本章主要内容 ·  介绍多媒体系统中媒体文件扫描的工作原理. 本章涉及的源代码文件名及位置 下面是本章分析的源码文件名及其位置. ·  MediaProvider.java packages/providers/MediaProvider/MediaProvider.

[深入理解Android卷一全文-第九章]深入理解Vold和Rild

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第9章  深入理解Vold和Rild 本章主要内容 ·  介绍Vold. ·  介绍Rild. 本章涉及的源代码文件名称及位置 下面是本章分析的源码文件名及其位置. ·  Main.cpp system/vold/Main.cpp ·  NetlinkManager.cpp system/vold/Netli

[深入理解Android卷二 全文-第八章]深入理解ContentService和AccountManagerService

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容 第8章  深入理解ContentService和AccountManagerService 本章主要内容: ·  介绍ContentService ·  介绍AccountManagerService 本章所涉及的源代码文件名及位置: ·  SystemServer.java frameworks/base/s

[深入理解Android卷一全文-第三章]深入理解init

因为<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第3章  深入理解init 本章主要内容 ·  深入分析init. 本章涉及的源代码文件名称及位置 以下是本章分析的源代码文件名称及其位置. ·  init.c system/core/init/init.c ·  parser.c system/core/init/parser.c ·  builtins.

[深入理解Android卷一全文-第一章]阅读前的准备工作

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. (出版社排版好的PDF版正在向出版社申请,到时候会通过CSDN下载资源发布) 第一章  阅读前的准备工作 本章主要内容 本章简单介绍Android系统架构.编译环境的搭建以及一些工具的使用. 1.1  系统架构 1.1.1  Android系统架构 Android是Google公司推出的一款手机开发平台.该平

[深入理解Android卷一全文-第四章]深入理解zygote

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的所有内容. 第4章  深入理解Zygote 本章主要内容 ·  深入分析zygote,并介绍system_server进程的初始化工作. 本章涉及的源代码文件名称及位置 以下是我们本章分析的源代码文件名称及其位置. ·  App_main.cpp framework/base/cmds/app_process/App_m

[深入理解Android卷一全文-第七章]深入理解Audio系统

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第7章  深入理解Audio系统 本章主要内容 ·  具体分析AudioTrack. ·  具体分析AudioFlinger. ·  具体分析AudioPolicyService. 本章涉及的源代码文件名称及位置 以下是本章分析的源代码文件名称及其位置. ·  AudioTrack.java framewor

[深入理解Android卷一全文-第五章]深入理解常见类

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在OSC博客中全文转发这两本书的全部内容. 第5章 深入理解常见类 本章主要内容 ·  分析RefBase.sp,wp和LightRefBase类. ·  分析Native的Thread类和常用同步类. ·  分析Java层的Handler.Looper,以及HandlerThread类. 本章涉及的源代码文件名称及位置 下面是我们本章分析的源码文件名和