阿拉神农 · 更新于 2018-11-28 11:00:43

第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显示了这二脉的关系:

image

图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所示:

image

图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所示:

image

图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:

image

图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描绘一下心中的想法:

image

图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函数,它非常简单,就是往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来表示:

image

图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函数完成按键的处理。