深入淺出Android開發之Surface介紹


一 目的

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

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

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

 Activity是如何顯示的

最初的想法就是,Activity獲得一塊顯存,然后在上面繪圖,最后交給設備去顯示。這個道理是沒錯,但是AndroidSurfaceFlinger是在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那幾行非常關鍵,它關系到咱們在ActivitysetContentView后,整個Window到底都包含了些什么。我先告訴大家。所有你創建的View之上,還有一個DecorView,這是一個FrameLayout,另外還有一個PhoneWindow。上面這些東西的代碼在

framework/Policies/Base/Phone/com/android/Internal/policy/impl。這些隱藏的View的創建都是由你在AcitivtyonCreate中調用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類型,

mContentParent是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()又返回什么呢?

PhoneWindowWindow中派生,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有那么點關系,其實它根本和ViewUI關系不大,它不過是一個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的關系。

ublic 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,所以接下來消息循環下一個將調用的就是ViewRoothandleMessage

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是不可能說清楚了。不過我們要先回顧下之前的關鍵步驟。

l         add中,new了一個SurfaceSession

l         創建new了一個Surface

l         調用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<SurfaceComposerClient> client = new SurfaceComposerClient;

client->incStrong(clazz);

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

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

}

Surfaceinit對應為:

[--->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<SurfaceControl> 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<SurfaceControl>& 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<SurfaceControl>& surface = getSurfaceControl(env, clazz);

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

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

        setSurfaceControl(env, clazz, rhs);

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

    }

}

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

那么Surface在哪里創建的呢?為了解釋這個問題,我使用了終極武器,aidl

終極武器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調用只是copyFromSurfaceControl對象到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的幫助,我無論如何也不會知道這其中的奧妙。

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

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

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

l         客戶端調用SurfacereadFromParcel,獲得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<SurfaceControl>& 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<Surface>& control(getSurface(env, clazz));

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

    sp<Surface> 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         調用Viewdraw,讓他們在這個Canvas上盡情繪圖才。另外,這個View會調用所有它的子View來畫圖,最終會進入到ViewonDraw函數中,在這里我們可以做定制化的界面美化工作。當然,如果你想定制化整個系統畫圖的話,完全可以把performTranvsal看懂,然后再修改。

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

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

總結

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM