深入淺出-詳解Android Surface系統

一 目的

 

本篇文章的目的就是為瞭講清楚Android中的Surface系統,大傢耳熟能詳的SurfaceFlinger到底是個什麼東西,它的工作流程又是怎樣的。當然,鑒於SurfaceFlinger的復雜性,我們依然將采用情景分析的辦法,找到合適的切入點。

 

一個Activity是怎麼在屏幕上顯示出來的呢?我將首先把這個說清楚。

 

接著我們把其中的關鍵調用抽象在Native層,以這些函數調用為切入點來研究SurfaceFlinger。好瞭,開始我們的征途吧。

 

二 Activity是如何顯示的

 

最初的想法就是,Activity獲得一塊顯存,然後在上面繪圖,最後交給設備去顯示。這個道理是沒錯,但是Android的SurfaceFlinger是在System Server進程中創建的,Activity一般另有線程,這之間是如何…如何掛上關系的呢?我可以先提前告訴大傢,這個過程還比較復雜。

 

好吧,我們從Activity最初的啟動開始。代碼在framework/base/core/java/android/app/ActivityThread.java中,這裡有個函數叫handleLaunchActivity。

 

[—->ActivityThread:: handleLaunchActivity()]

 

private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {  

 

      Activity a = performLaunchActivity(r, customIntent);  

 

   

 

        if (a != null) {  

 

            r.createdConfig = new Configuration(mConfiguration);  

 

            Bundle oldState = r.state;  

 

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

 

—->調用handleResumeActivity  

 

handleLaunchActivity中會調用handleResumeActivity。

 

[—>ActivityThread:: handleResumeActivity]

 

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 decor = r.window.getDecorView();  

 

                decor.setVisibility(View.INVISIBLE);  

 

                ViewManager wm = a.getWindowManager();  

 

                WindowManager.LayoutParams l = r.window.getAttributes();  

 

                a.mDecor = decor;  

 

                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;  

 

                if (a.mVisibleFromClient) {  

 

                    a.mWindowAdded = true;  

 

                    wm.addView(decor, l); //這個很關鍵。  

 

                } 

上面addView那幾行非常關鍵,它關系到咱們在Activity中setContentView後,整個Window到底都包含瞭些什麼。我先告訴大傢。所有你創建的View之上,還有一個DecorView,這是一個FrameLayout,另外還有一個PhoneWindow。上面這些東西的代碼在framework/Policies/Base/Phone/com/android/Internal/policy/impl。這些隱藏的View的創建都是由你在Acitivty的onCreate中調用setContentView導致的。

 

[—->PhoneWindow:: addContentView]

 

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

 

if (mContentParent == null) { //剛創建的時候mContentParent為空  

 

installDecor();  

 

}  

 

mContentParent.addView(view, params);  

 

final Callback cb = getCallback();  

 

if (cb != null) {  

 

cb.onContentChanged();  

 

}  

 

installDecor將創建mDecor和mContentParent。mDecor是DecorView類型,ContentParent是ViewGroup類型

 

private void installDecor() {  

 

if (mDecor == null) {  

 

mDecor = generateDecor();  

 

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);  

 

mDecor.setIsRootNamespace(true);  

 

}  

 

if (mContentParent == null) {  

 

mContentParent = generateLayout(mDecor); 

那麼,ViewManager wm = a.getWindowManager()又返回什麼呢?

 

PhoneWindow從Window中派生,Acitivity創建的時候會調用它的setWindowManager。而這個函數由Window類實現。

 

代碼在framework/base/core/java/android/view/Window.java中:

 

public void setWindowManager(WindowManager wm,IBinder appToken, String appName) {  

 

mAppToken = appToken;  

 

mAppName = appName;  

 

if (wm == null) {  

 

wm = WindowManagerImpl.getDefault();  

 

}  

 

mWindowManager = new LocalWindowManager(wm);  

 

你看見沒,分析JAVA代碼這個東西真的很復雜。mWindowManager的實現是LocalWindowManager,但由通過Bridge模式把功能交給WindowManagerImpl去實現瞭。

 

真的很復雜!

 

好瞭,我們回到wm.addView(decor, l)。最終會由WindowManagerImpl來完成addView操作,我們直接看它的實現好瞭。

 

代碼在framework/base/core/java/android/view/WindowManagerImpl.java:

 

[—->addView]

 

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

 

{  

 

ViewRoot root; //ViewRoot,我們的主人公終於登場!  

 

synchronized (this) {  

 

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;  

 

mParams[index] = wparams;  

 

}  

 

root.setView(view, wparams, panelParentView);  

 

ViewRoot是整個顯示系統中最為關鍵的東西,看起來這個東西好像和View有那麼點關系,其實它根本和View等UI關系不大,它不過是一個Handler罷瞭,唯一有關系的就是它其中有一個變量為Surface類型。我們看看它的定義。ViewRoot代碼在framework/base/core/java/android/view/ViewRoot.java中:

 

public final class ViewRoot extends Handler implements ViewParent,  

 

View.AttachInfo.Callbacks  

 

{  

 

private final Surface mSurface = new Surface();  

 

它竟然從handler派生,而ViewParent不過定義瞭一些接口函數罷瞭。

 

看到Surface直覺上感到它和SurfaceFlinger有點關系。要不先去看看?

 

Surface代碼在framework/base/core/java/android/view/Surface.java中,我們調用的是無參構造函數。

 

public Surface() {  

 

mCanvas = new CompatibleCanvas(); //就是創建一個Canvas!  

 

如果你有興趣的話,看看Surface其他構造函數,最終都會調用native的實現,而這些native的實現將和SurfaceFlinger建立關系,但我們這裡ViewRoot中的mSurface顯然還沒有到這一步。那它到底是怎麼和SurfaceFlinger搞上的呢?這一切待會就會水落石出的。

 

另外,為什麼ViewRoot是主人公呢?因為ViewRoot建立瞭客戶端和SystemServer的關系。我們看看它的構造函數。

 

public ViewRoot(Context context) {  

 

super();  

 

….  

 

getWindowSession(context.getMainLooper());  

 

getWindowsession將建立和WindowManagerService的關系。

 

public static IWindowSession getWindowSession(Looper mainLooper) {  

 

        synchronized (mStaticInit) {  

 

            if (!mInitialized) {  

 

                try {  

 

                //sWindowSession是通過Binder機制創建的。終於讓我們看到點希望瞭  

 

                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);  

 

                    sWindowSession = IWindowManager.Stub.asInterface(  

 

                            ServiceManager.getService("window"))  

 

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

 

                    mInitialized = true;  

 

                } catch (RemoteException e) {  

 

                }  

 

            }  

 

            return sWindowSession;  

 

        }  

 

    } 

上面跨Binder的進程調用另一端是WindowManagerService,代碼在framework/base/services/java/com/android/server/WindowManagerService.java中。我們先不說這個。

 

回過頭來看看ViewRoot接下來的調用。

 

[–>ViewRoot::setView()],這個函數很復雜,我們看其中關鍵幾句。

 

public void setView(View view, WindowManager.LayoutParams attrs,  

 

            View panelParentView) {  

 

        synchronized (this) {  

 

            requestLayout();   

 

                try {  

 

                    res = sWindowSession.add(mWindow, mWindowAttributes,  

 

                            getHostVisibility(), mAttachInfo.mContentInsets);  

 

                }   

 

requestLayout實現很簡單,就是往handler中發送瞭一個消息。

 

public void requestLayout() {  

 

        checkThread();  

 

        mLayoutRequested = true;  

 

        scheduleTraversals(); //發送DO_TRAVERSAL消息  

 

}   

 

public void scheduleTraversals() {  

 

        if (!mTraversalScheduled) {  

 

            mTraversalScheduled = true;  

 

            sendEmptyMessage(DO_TRAVERSAL);  

 

        }  

 

我們看看跨進程的那個調用。sWindowSession.add。它的最終實現在WindowManagerService中。

 

[—>WindowSession::add()]

 

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

 

                int viewVisibility, Rect outContentInsets) {  

 

            return addWindow(this, window, attrs, viewVisibility, outContentInsets);  

 

        } 

WindowSession是個內部類,會調用外部類的addWindow。

 

這個函數巨復雜無比,但是我們的核心目標是找到創建顯示相關的部分。所以,最後精簡的話就簡單瞭。

 

[—>WindowManagerService:: addWindow]

 

public int addWindow(Session session, IWindow client,  

 

            WindowManager.LayoutParams attrs, int viewVisibility,  

 

            Rect outContentInsets) {  

 

        //創建一個WindowState,這個又是什麼玩意兒呢?  

 

              win = new WindowState(session, client, token,  

 

                    attachedWindow, attrs, viewVisibility);  

 

           win.attach();  

 

           return res;  

 

WindowState類中有一個和Surface相關的成員變量,叫SurfaceSession。它會在attach函數中被創建。SurfaceSession嘛,就和SurfaceFlinger有關系瞭。我們待會看。

 

好,我們知道ViewRoot創建及調用add後,我們客戶端的View系統就和WindowManagerService建立瞭牢不可破的關系。

 

另外,我們知道ViewRoot是一個handler,而且剛才我們調用瞭requestLayout,所以接下來消息循環下一個將調用的就是ViewRoot的handleMessage。

 

public void handleMessage(Message msg) {  

 

        switch (msg.what) {  

 

       case DO_TRAVERSAL:  

 

            performTraversals(); 

performTraversals更加復雜無比,經過我仔細挑選,目標鎖定為下面幾個函數。當然,後面我們還會回到performTraversals,不過我們現在更感興趣的是Surface是如何創建的。

 

private void performTraversals() {  

 

        // cache mView since it is used so much below…  

 

        final View host = mView;  

 

   

 

         boolean initialized = false;  

 

            boolean contentInsetsChanged = false;  

 

            boolean visibleInsetsChanged;  

 

            try {  

 

//ViewRoot也有一個Surface成員變量,叫mSurface,這個就是代表SurfaceFlinger的客戶端  

 

//ViewRoot在這個Surface上作畫,最後將由SurfaceFlinger來合成顯示。剛才說瞭mSurface還沒有什麼內容。  

 

          relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); 

[—->ViewRoot:: relayoutWindow()]

 

private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,  

 

            boolean insetsPending) throws RemoteException {  

 

         

 

//relayOut是跨進程調用,mSurface做為參數傳進去瞭,看來離真相越來越近瞭呀!  

 

        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做為參數傳進去瞭。  

 

       } 

我們趕緊轉到WindowManagerService去看看吧。

 

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, Surface outSurface){  

 

               …..  

 

         try {  

 

           //看到這裡,我內心一陣狂喜,有戲,太有戲瞭!  

 

         //其中win是我們最初創建的WindowState!  

 

                    Surface surface = win.createSurfaceLocked();  

 

                    if (surface != null) {  

 

                  //先創建一個本地surface,然後把傳入的參數outSurface copyFrom一下  

 

                        outSurface.copyFrom(surface);  

 

                        win.mReportDestroySurface = false;  

 

                        win.mSurfacePendingDestroy = false;  

 

                       } else {  

 

                       outSurface.release();  

 

                    }  

 

                }  

 

[—>WindowState::createSurfaceLocked]

 

Surface createSurfaceLocked() {  

 

            

 

                try {  

 

                    mSurface = new Surface(  

 

                            mSession.mSurfaceSession, mSession.mPid,  

 

                            mAttrs.getTitle().toString(),  

 

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

 

                  }   

 

                Surface.openTransaction(); 

這裡使用瞭Surface的另外一個構造函數。

 

    public Surface(SurfaceSession s,  

 

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

 

        throws OutOfResourcesException {  

 

        mCanvas = new CompatibleCanvas();  

 

        init(s,pid,name,display,w,h,format,flags); —->調用瞭native的init函數。  

 

        mName = name;  

 

到這裡,不進入JNI是不可能說清楚瞭。不過我們要先回顧下之前的關鍵步驟。

 

◆ add中,new瞭一個SurfaceSession

 

◆創建new瞭一個Surface

 

◆調用copyFrom,把本地Surface信息傳到outSurface中 

 

 

JNI層

 

上面兩個類的JNI實現都在framework/base/core/jni/android_view_Surface.cpp中。

 

[—->SurfaceSession:: SurfaceSession()]

 

public class SurfaceSession {  

 

/** Create a new connection with the surface flinger. */ 

 

public SurfaceSession() {  

 

init();  

 

它的init函數對應為:

 

[—>SurfaceSession_init]

 

static void SurfaceSession_init(JNIEnv* env, jobject clazz)  

 

{  

 

//SurfaceSession對應為SurfaceComposerClient  

 

sp client = new SurfaceComposerClient;  

 

client->incStrong(clazz);  

 

//Google常用做法,在JAVA對象中保存C++對象的指針。  

 

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

 

Surface的init對應為:

 

[—>Surface_init]

 

static void Surface_init(  

 

JNIEnv* env, jobject clazz,  

 

jobject session,  

 

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

 

{  

 

SurfaceComposerClient* client =  

 

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

 

sp surface;  

 

if (jname == NULL) {  

 

//client是SurfaceComposerClient,返回的surface是一個SurfaceControl  

 

//真得很復雜!  

 

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

 

} else {  

 

const jchar* str = env->GetStringCritical(jname, 0);  

 

const String8 name(str, env->GetStringLength(jname));  

 

env->ReleaseStringCritical(jname, str);  

 

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

 

}  

 

//把surfaceControl信息設置到Surface對象中  

 

setSurfaceControl(env, clazz, surface);  

 

static void setSurfaceControl(JNIEnv* env, jobject clazz,  

 

const sp& surface)  

 

{  

 

SurfaceControl* const p =  

 

(SurfaceControl*)env->GetIntField(clazz, so.surfaceControl);  

 

if (surface.get()) {  

 

surface->incStrong(clazz);  

 

}  

 

if (p) {  

 

p->decStrong(clazz);  

 

}  

 

env->SetIntField(clazz, so.surfaceControl, (int)surface.get());  

 

[—>Surface_copyFrom]

 

static void Surface_copyFrom(  

 

JNIEnv* env, jobject clazz, jobject other)  

 

{  

 

const sp& surface = getSurfaceControl(env, clazz);  

 

const sp& rhs = getSurfaceControl(env, other);  

 

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

 

setSurfaceControl(env, clazz, rhs);  

 

//把本地那個surface的surfaceControl對象轉移到outSurface上  

 

}  

 

這裡僅僅是surfaceControl的轉移,但是並沒有看到Surface相關的信息。

 

那麼Surface在哪裡創建的呢?為瞭解釋這個問題,我使用瞭終極武器,aidl。

 

1 終極武器AIDL

 

aidl可以把XXX.aidl文件轉換成對應的java文件。我們剛才調用的是WindowSession的

 

relayOut函數。如下:

 

sWindowSession.relayout(  

 

mWindow, params,  

 

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

 

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

 

viewVisibility, insetsPending, mWinFrame,  

 

mPendingContentInsets, mPendingVisibleInsets,  

 

mPendingConfiguration, mSurface); 

它的aidl文件在framework/base/core/java/android/view/IWindowSession.aidl中

 

interface IWindowSession {  

 

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

 

in int viewVisibility, out Rect outContentInsets);  

 

void remove(IWindow window);  

 

//註意喔,這個outSurface前面的是out,表示輸出參數,這個類似於C++的引用。  

 

int relayout(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); 

剛才說瞭,JNI及其JAVA調用隻是copyFrom瞭SurfaceControl對象到outSurface中,但是沒看到哪裡創建Surface。這其中的奧秘就在aidl文件編譯後生成的java文件中。

 

你在命令行下可以輸入:

 

aidl -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\ -Id:\android-2.2-froyo-20100625-source\source\frameworks\base\Graphics\java d:\android-2.2-froyo-20100625-source\source\frameworks\base\core\java\android\view\IWindowSession.aidl test.java

 

以生成test.java文件。-I參數指定include目錄,例如aidl有些參數是在別的java文件中指定的,那麼這個-I就需要把這些目錄包含進來。

 

先看看ViewRoot這個客戶端生成的代碼是什麼。

 

public int relayout(  

 

android.view.IWindow window,  

 

android.view.WindowManager.LayoutParams attrs,  

 

int requestedWidth, int requestedHeight,  

 

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個參數  

 

throws android.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  

 

}  

 

}  

 

finally {  

 

_reply.recycle();  

 

_data.recycle();  

 

}  

 

return _result;  

 

真奇怪啊,Binder客戶端這頭竟然沒有把outSurface的信息發過去。我們趕緊看看服務端。

 

服務端這邊處理是在onTranscat函數中。

 

@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  

 

{  

 

switch (code)  

 

{  

 

case TRANSACTION_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瞭,那怎麼傳到客戶端呢?  

 

if ((_arg10!=null)) {  

 

reply.writeInt(1);//調用Surface的writeToParcel,把信息加入reply  

 

_arg10.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);  

 

}  

 

return true;  

 

太詭異瞭!竟然有這麼多花花腸子。我相信如果沒有aidl的幫助,我無論如何也不會知道這其中的奧妙。

 

那好,我們的流程明白瞭。

 

◆客戶端雖然傳瞭一個surface,但其實沒傳遞給服務端

 

◆服務端調用writeToParcel,把信息寫到Parcel中,然後數據傳回客戶端

 

◆客戶端調用Surface的readFromParcel,獲得surface信息。

 

那就去看看writeToParcel吧。

 

[—->Surface_writeToParcel]

 

static void Surface_writeToParcel(  

 

JNIEnv* env, jobject clazz, jobject argParcel, jint flags)  

 

{  

 

Parcel* parcel = (Parcel*)env->GetIntField(  

 

argParcel, no.native_parcel);  

 

const sp& control(getSurfaceControl(env, clazz));  

 

//還好,隻是把數據序列化到Parcel中  

 

SurfaceControl::writeSurfaceToParcel(control, parcel);  

 

if (flags & PARCELABLE_WRITE_RETURN_VALUE) {  

 

setSurfaceControl(env, clazz, 0);  

 

}  

 

那看看客戶端的Surface_readFromParcel吧。

 

[—–>Surface_readFromParcel]

 

static void Surface_readFromParcel(  

 

JNIEnv* env, jobject clazz, jobject argParcel)  

 

{  

 

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

 

//客戶端這邊還沒有surface呢  

 

const sp& control(getSurface(env, clazz));  

 

//不過我們看到希望瞭,根據服務端那邊Parcel信息來構造一個新的surface  

 

sp rhs = new Surface(*parcel);  

 

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

 

setSurface(env, clazz, rhs); //把這個新surface賦給客戶端。終於我們有瞭surface!  

 

}  

 

到此,我們終於七拐八繞的得到瞭surface,這其中經歷太多曲折瞭。下一節,我們將精簡這其中復雜的操作,統一歸到Native層,以這樣為切入點來瞭解Surface的工作流程和原理。

 

好,反正你知道ViewRoot調用瞭relayout後,Surface就真正從WindowManagerService那得到瞭。繼續回到ViewRoot,其中還有一個重要地方是我們知道卻不瞭解的。

 

private void performTraversals() {  

 

// cache mView since it is used so much below…  

 

final View host = mView;  

 

boolean initialized = false;  

 

boolean contentInsetsChanged = false;  

 

boolean visibleInsetsChanged;  

 

try {  

 

relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);  

 

// relayoutWindow完後,我們得到瞭一個無比寶貴的Surface  

 

//那我們畫界面的地方在哪裡?就在這個函數中,離relayoutWindow不遠處。  

 

….  

 

boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();  

 

if (!cancelDraw && !newSurface) {  

 

mFullRedrawNeeded = false;  

 

draw(fullRedrawNeeded); //draw?draw什麼呀?  

 

[—>ViewRoot::draw()]

 

private void draw(boolean fullRedrawNeeded) {  

 

Surface surface = mSurface; //嘿嘿,不擔心瞭,surface資源都齊全瞭  

 

if (surface == null || !surface.isValid()) {  

 

return;  

 

}  

 

if (mAttachInfo.mViewScrollChanged) {  

 

mAttachInfo.mViewScrollChanged = false;  

 

mAttachInfo.mTreeObserver.dispatchOnScrollChanged();  

 

}  

 

int yoff;  

 

final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();  

 

if (scrolling) {  

 

yoff = mScroller.getCurrY();  

 

} else {  

 

yoff = mScrollY;  

 

}  

 

if (mCurScrollY != yoff) {  

 

mCurScrollY = yoff;  

 

fullRedrawNeeded = true;  

 

}  

 

float appScale = mAttachInfo.mApplicationScale;  

 

boolean scalingRequired = mAttachInfo.mScalingRequired;  

 

Rect dirty = mDirty;  

 

if (mUseGL) { //我們不用OPENGL  

 

…  

 

}  

 

Canvas canvas;  

 

try {  

 

int left = dirty.left;  

 

int top = dirty.top;  

 

int right = dirty.right;  

 

int bottom = dirty.bottom;  

 

//從Surface中鎖定一塊區域,這塊區域是我們認為的需要重繪的區域  

 

canvas = surface.lockCanvas(dirty);  

 

// TODO: Do this in native  

 

canvas.setDensity(mDensity);  

 

}  

 

try {  

 

if (!dirty.isEmpty() || mIsAnimating) {  

 

long startTime = 0L;  

 

try {  

 

canvas.translate(0, -yoff);  

 

if (mTranslator != null) {  

 

mTranslator.translateCanvas(canvas);  

 

}  

 

canvas.setScreenDensity(scalingRequired  

 

? DisplayMetrics.DENSITY_DEVICE : 0);  

 

//mView就是之前的decoreView,  

 

mView.draw(canvas);  

 

}  

 

} finally {  

 

//我們的圖畫完瞭,告訴surface釋放這塊區域  

 

surface.unlockCanvasAndPost(canvas);  

 

}  

 

if (scrolling) {  

 

mFullRedrawNeeded = true;  

 

scheduleTraversals();  

 

}  

 

看起來,這個surface的用法很簡單嘛:

 

l lockSurface,得到一個畫佈Canvas

 

l 調用View的draw,讓他們在這個Canvas上盡情繪圖才。另外,這個View會調用所有它的子View來畫圖,最終會進入到View的onDraw函數中,在這裡我們可以做定制化的界面美化工作。當然,如果你想定制化整個系統畫圖的話,完全可以把performTranvsal看懂,然後再修改。

 

l unlockCanvasAndPost,告訴Surface釋放這塊畫佈

 

當然,這幾個重要函數調用幹瞭具體的活。這些重要函數,我們最終會精簡到Native層的。

 

2 總結

 

到這裡,你應該知道瞭一個Activity中,調用setContentView後它如何從系統中獲取一塊Surface,以及它是如何使用這個Surface的瞭。不得不說,關於UI這塊,Android絕對是夠復雜的。難怪2.3把UI這塊代碼基本重寫一遍,希望能夠簡單精煉點。

發佈留言