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

因爲《深刻理解Android 卷一》和《深刻理解Android卷二》再也不出版。而知識的傳播不該該因爲紙質媒介的問題而中斷,因此我將在CSDN博客中全文轉發這兩本書的所有內容。


第8章  深刻理解Surface系統

本章主要內容java

·  具體分析一個Activity的顯示過程。android

·  具體分析Surface。算法

·  具體分析SurfaceFlinger。數據庫

本章涉及的源代碼文件名稱及位置:編程

· ActivityThread.javacanvas

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

·  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所看到的:


圖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所看到的:


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


圖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所看到的 :


圖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。後文將再也不深刻探討和硬件平臺有關的知識。


圖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來觀察整體的工做流程是如何的。


圖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所看到的:


圖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來表示:


圖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的差異。

最近網絡中流行的一種文體,其特色就是會用很是多感嘆號。

相關文章
相關標籤/搜索