SurfaceView原理分析 一、概述 image.png 二、使用SurfaceView 三、實現原理

一、概述

1、SurfaceView

SurfaceView從Android 1.0(API level 1)時就有 。它繼承自類View,因此它本質上是一個View。但與普通View不同的是,它有自己的Surface。我們知道,一般的Activity包含的多個View會組成View hierachy的樹形結構,只有最頂層的DecorView,也就是根結點視圖,纔是對WMS可見的。這個DecorView在WMS中有一個對應的WindowState。相應地,在SF中對應的Layer。而SurfaceView自帶一個Surface,這個Surface在WMS中有自己對應的WindowState,在SF中也會有自己的Layer。

也就是說,雖然在App端它仍在View hierachy中,但在Server端(WMS和SF),它與宿主窗口是分離的。這樣的好處是對這個Surface的渲染可以放到單獨線程去做,渲染時可以有自己的GL context。這對於一些遊戲、視頻等性能相關的應用非常有益,因爲它不會影響主線程對事件的響應。但它也有缺點,因爲這個Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進行平移,縮放等變換,也不能放在其它ViewGroup中,一些View中的特性也無法使用。

從上圖不難難看出,通過SurfaceFlinger中的Layer來繪製自己的UI到宿主窗口上的。SurfaceView就是在窗口上挖一個洞,它就是顯示在這個洞裏,其他的View是顯示在窗口上,所以View可以顯式在 SurfaceView之上,你也可以添加一些層在SurfaceView之上(其實並不是SurfaceView在Z軸上真正的對宿主窗口表面挖了個洞,而只是在其宿主Activity窗口上設置了一塊透明區域罷了)。

從API中可以看出SurfaceView屬於View的子類它是專門爲製作遊戲而產生的,它的功能非常強大,最重要的是它支持OpenGL ES庫,2D和3D的效果都可以實現。創建SurfaceView的時候需要實現SurfaceHolder.Callback接口,它可以用來監聽SurfaceView的狀態,比如:SurfaceView的改變 、SurfaceView的創建 、SurfaceView 銷燬等,我們可以在相應的方法中做一些比如初始化的操作或者清空的操作等等。

SurfaceView的Surface排在Window的Surface(也就是View樹所在的繪圖層)的下面,SurfaceView嵌入到Window的View結構樹中就好像在Window的Surface上強行打了個洞讓自己顯示到屏幕上,而且SurfaceView另起一個線程對自己的Surface進行刷新。需要注意的是SurfaceHolder.Callback的所有回調方法都是在主線程中回調的。

SurfaceView、SurfaceHolder、Surface的關係可以概括爲以下幾點:

  • SurfaceView是擁有獨立繪圖層的特殊View。
  • Surface就是指SurfaceView所擁有的那個繪圖層,其實它就是內存中的一段繪圖緩衝區。
  • SurfaceView中具有兩個Surface,也就是我們所說的雙緩衝機制
  • SurfaceHolder顧名思義就是Surface的持有者,SurfaceView就是通過過SurfaceHolder來對Surface進行管理控制的。並且SurfaceView.getHolder方法可以獲取SurfaceView相應的SurfaceHolder。
  • Surface是在SurfaceView所在的Window可見的時候創建的。我們可以使用SurfaceHolder.addCallback方法來監聽Surface的創建與銷燬的事件。

2、GLSurfaceView

GLSurfaceView從Android 1.5(API level 3)開始加入,作爲SurfaceView的補充。它可以看作是SurfaceView的一種典型使用模式。在SurfaceView的基礎上,它加入了EGL的管理,並自帶了渲染線程。另外它定義了用戶需要實現的Render接口,作爲GLSurfaceView的Client,只需要將實現了渲染函數的Renderer的實現類設置給GLSurfaceView即可。

3、SurfaceTexture

SurfaceTexture從Android 3.0(API level 11)加入。和SurfaceView不同的是,它對圖像流的處理並不直接顯示,而是轉爲GL外部紋理,因此可用於圖像流數據的二次處理(如Camera濾鏡,桌面特效等)。比如Camera的預覽數據,變成紋理後可以交給GLSurfaceView直接顯示,也可以通過SurfaceTexture交給TextureView作爲View heirachy中的一個硬件加速層來顯示。首先,SurfaceTexture從圖像流(來自Camera預覽,視頻解碼,GL繪製場景等)中獲得幀數據,當調用updateTexImage()時,根據內容流中最近的圖像更新SurfaceTexture對應的GL紋理對象,接下來,就可以像操作普通GL紋理一樣操作它了。從下面的類圖中可以看出,它核心管理着一個BufferQueue的Consumer和Producer兩端。Producer端用於內容流的源輸出數據,Consumer端用於拿GraphicBuffer並生成紋理。SurfaceTexture.OnFrameAvailableListener用於讓SurfaceTexture的使用者知道有新數據到來。JNISurfaceTextureContext是OnFrameAvailableListener從Native到Java的JNI跳板。其中SurfaceTexture中的attachToGLContext()和detachToGLContext()可以讓多個GL context共享同一個內容源。

4、TextureView

TextureView在4.0(API level 14)中引入。它可以將內容流直接投影到View中,可以用於實現Live preview等功能。和SurfaceView不同,它不會在WMS中單獨創建窗口,而是作爲View hierachy中的一個普通View,因此可以和其它普通View一樣進行移動,旋轉,縮放,動畫等變化。值得注意的是TextureView必須在硬件加速的窗口中。它顯示的內容流數據可以來自App進程或是遠端進程。TextureView繼承自View,它與其它的View一樣在View hierachy中管理與繪製。TextureView重載了draw()方法,其中主要把SurfaceTexture中收到的圖像數據作爲紋理更新到對應的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用於通知TextureView內容流有新圖像到來。SurfaceTextureListener接口用於讓TextureView的使用者知道SurfaceTexture已準備好,這樣就可以把SurfaceTexture交給相應的內容源。Surface爲BufferQueue的Producer接口實現類,使生產者可以通過它的軟件或硬件渲染接口爲SurfaceTexture內部的BufferQueue提供graphic buffer。

二、使用SurfaceView

通過SurfaceHolder對象的lockCanvans()方法,我們可以獲取當前的Canvas繪圖對象。接下來的操作就和自定義View中的繪圖操作一樣了。需要注意的是這裏獲取到的Canvas對象還是繼續上次的Canvas對象,而不是一個新的對象。因此,之前的繪圖操作都會被保留,如果需要擦除,則可以在繪製前,通過drawColor()方法來進行清屏操作。

public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
  // SurfaceHolder
  private SurfaceHolder mSurfaceHolder;
  // 用於繪圖的Canvas
  private Canvas mCanvas;
  // 子線程標誌位
  private boolean mIsDrawing;

  public CustomSurfaceView(Context context) {
    super(context);
    initView();
  }

  public CustomSurfaceView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initView();
  }

  private void initView() {
    mSurfaceHolder = getHolder();
    mSurfaceHolder.addCallback(this);
    setFocusable(true);
    setFocusableInTouchMode(true);
    this.setKeepScreenOn(true);
  }

  @Override
  public void surfaceCreated(SurfaceHolder holder) {
    mIsDrawing = true;
    new Thread(new Runnable() {
      @Override
      public void run() {
        while (mIsDrawing) {
          try {
            mCanvas = mSurfaceHolder.lockCanvas();
            // draw sth繪製過程
          } catch (Exception e) {
            e.printStackTrace();
          } finally {
            //保證每次都將繪圖的內容提交
            if (mCanvas != null)
              mSurfaceHolder.unlockCanvasAndPost(mCanvas);
          }
        }
      }
    }).start();
  }

  @Override
  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

  }

  @Override
  public void surfaceDestroyed(SurfaceHolder holder) {
    mIsDrawing = false;
  }
}

三、實現原理

1、Window、Surface創建過程

由於SurfaceView具有獨立的surface不與它的宿主窗口共享同一surface,因此,在它的UI內容可以繪製之前,必須先將它自己的surface創建出來。但是SurfaceView仍然是屬於宿主窗口的視圖結構的一個結點的,也就是說,SurfaceView仍然是會參與到宿主窗口的某些執行流程中。

每當一個窗口需要刷新UI時,就會調用ViewRoot類的performTraversals。ViewRoot類的成員函數performTraversals在執行的過程中,如果發現當前窗口的surface還沒有創建,或者發現當前窗口的繪圖表面已經失效了,那麼就會請求WindowManagerService服務創建一個新surface,同時,它還會通過一系列的回調函數來讓嵌入在窗口裏面的SurfaceView有機會創建自己的surface。

  1. ViewRootImpl.performTraversals()
private void performTraversals() {
    //變量host與變量mView指向的是同一個DecorView對象,這個DecorView對象描述的當前窗口的頂層視圖。
    final View host = mView;
    ...
    if (mFirst) {
        //每一個視圖附加到它的宿主窗口的時候,都會獲得一個AttachInfo對象,用來描述被附加的窗口的信息。
         host.dispatchAttachedToWindow(mAttachInfo, 0);
    }
    ...
    if (viewVisibilityChanged) {
        mAttachInfo.mWindowVisibility = viewVisibility;
        host.dispatchWindowVisibilityChanged(viewVisibility);
    }
    ....
    
}

SurfaceView雖然有自己獨立的surface進行ui的繪製,但是它仍然是viewTree中的一個節點,一些方法的調用還是要在主窗口繪製流程中被調用的

  1. SurfaceView.onAttachedToWindow()
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mParent.requestTransparentRegion(this);
    mSession = getWindowSession();
    mLayout.token = getWindowToken();
    mLayout.setTitle("SurfaceView");
    mViewVisibility = getVisibility() == VISIBLE;

    if (!mGlobalListenersAdded) {
        ViewTreeObserver observer = getViewTreeObserver();
        observer.addOnScrollChangedListener(mScrollChangedListener);
        observer.addOnPreDrawListener(mDrawListener);
        mGlobalListenersAdded = true;
    }
}

SurfaceView類的成員函數onAttachedToWindow做了兩件重要的事。
• 通知父視圖當前正在處理的SurfaceView需要在宿主窗口上挖一個洞,即需要在宿主窗口上設置一塊透明區域。
• 調用從父類View繼承下來的成員函數getWindowSession來獲得一個實現了IWindowSession接口的Binder代理對象,並且將該Binder代理對象保存在SurfaceView類的成員變量mSession中。每一個應用程序進程都有一個實現了IWindowSession接口的Binder代理對象,這個Binder代理對象是用來與WindowManagerService服務進行通信的,View類的成員函數getWindowSession返回的就是該Binder代理對象。

  1. SurfaceView.onWindowVisibilityChanged()
protected void onWindowVisibilityChanged(int visibility) {
    super.onWindowVisibilityChanged(visibility);
    mWindowVisibility = visibility == VISIBLE;
    mRequestedVisible = mWindowVisibility && mViewVisibility;
    updateWindow(false, false);
}

SurfaceView類的成員函數onWindowVisibilityChanged就會調用另外一個成員函數updateWindow來更新當前正在處理的SurfaceView。updateWindow的主要作用是SurfaceView更新的處理,包括mWindow,還有Surface的創建,更新,銷燬的通知回調等

2、SurfaceView如何挖洞

SurfaceView的窗口類型一般都是TYPE_APPLICATION_MEDIA或者TYPE_APPLICATION_MEDIA_OVERLAY,它的Z軸位置是小於其宿主窗口的Z位置。爲了保證SurfaceView的UI是可見的,SurfaceView就需要在其宿主窗口的上面挖一個洞出來,實際上就是在其宿主窗口的繪圖表面上設置一塊透明區域,以便可以將自己顯示出來。
SurfaceView在被附加到宿主窗口之上的時候,會請求在宿主窗口上設置透明區域,而每當其宿主窗口刷新自己的UI的時候,就會將所有嵌入在它裏面的SurfaceView所設置的透明區域收集起來,然後再通知WindowManagerService服務爲其設置一個總的透明區域。


  1. 請求在宿主窗口上挖一個洞
public class SurfaceView extends View {

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        // 1.請求透明區域,mParent表示父視圖,這裏就是DecorView,
        // DecorView類函requestTransparentRegion是從父類ViewGroup繼承下來的
        mParent.requestTransparentRegion(this);
        ....
    }
}

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    @Override
    public void requestTransparentRegion(View child) {
        if (child != null) {
            //2.將mPrivateFlags的值的View.REQUEST_TRANSPARENT_REGIONS位設置爲1,表示它要在宿主窗口上設置透明區域,
            //接着再調用從父類View的requestTransparentRegion來繼續向上請求設置透明區域
            child.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
            if (mParent != null) {
                mParent.requestTransparentRegion(this);
            }
        }
    }
}

//3.最後會找到ViewRootImpl
public final class ViewRootImpl implements ViewParent {
    public void requestTransparentRegion(View child) {
        // 檢查是否再主線程調用
        checkThread();
        if (mView == child) {
            mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
            mWindowAttributesChanged = true;
            mWindowAttributesChangesFlag = 0;
            // 調用requestLayout來刷新一下窗口的UI,對窗口的UI進行重新佈局和繪製。
            requestLayout();
        }
    }
    
    private void performTraversals() {
        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            performLayout(lp, mWidth, mHeight);

            // 計算透明區域
            if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
                //獲取頂層視圖的位置和大小
                host.getLocationInWindow(mTmpLocation);
                // 將頂層視圖所佔據的區域作爲窗口的初始化透明區域
                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
                        mTmpLocation[0] + host.mRight - host.mLeft,
                        mTmpLocation[1] + host.mBottom - host.mTop);
                // 從頂層視圖開始,從上到下收集每一個子視圖所要設置的區域,
                // 最終收集到的總透明區域保存在ViewRoot類的成員變量mTransparentRegion中。
                host.gatherTransparentRegion(mTransparentRegion);
                if (mTranslator != null) {
                    mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
                }
                // 檢查透明區域是否有變化
                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
                    mPreviousTransparentRegion.set(mTransparentRegion);
                    mFullRedrawNeeded = true;
                    // 通知WindowManagerService爲窗口設置由成員變量mTransparentRegion所指定的透明區域
                    try {
                        mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
                    } catch (RemoteException e) {
                    }
                }
            }
        }
    }
}
  1. SurfaceView.gatherTransparentRegion
@Override
public boolean gatherTransparentRegion(Region region) {
    if (isAboveParent() || !mDrawFinished) {
        return super.gatherTransparentRegion(region);
    }

    boolean opaque = true;
    if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
        // this view draws, remove it from the transparent region
        opaque = super.gatherTransparentRegion(region);
    } else if (region != null) {
        int w = getWidth();
        int h = getHeight();
        if (w>0 && h>0) {
            getLocationInWindow(mLocation);
            // otherwise, punch a hole in the whole hierarchy
            int l = mLocation[0];
            int t = mLocation[1];
            region.op(l, t, l+w, t+h, Region.Op.UNION);
        }
    }
    if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
        opaque = false;
    }
    return opaque;
}

3、如何繪製到屏幕上

View的繪製是由ViewRootImpl的Surface對象將繪製後的ui數據交給WindowManagerService,WindowManagerService會將多個Surface數據合併成一整屏的ui數據,交給SurfaceFlinger渲染對應的Layer。而SurfaceView內部維護着一塊Surface用於ui數據的的繪製,同時在WindowManagerService端會創建一個新的surface對象,對應着SurfaceFlinger的一個新的Layer,因此SurfaceView中繪製的數據就由新的Layer,而不是宿主DecorView的Layer;意思就是SurfaceView有和宿主DecorView對應的ViewRootImpl一樣的一套繪製渲染模型,兩者分別獨立渲染。
SurfaceView雖然具有獨立的surface,不過它仍然是宿主窗口的視圖結構中的一個結點,它仍然是可以參與到宿主窗口的繪製流程中去的。在繪製的過程中,每一個子視圖的成員函數draw或者dispatchDraw都會被調用到,以便它們可以繪製自己的UI。
SurfaceView類的成員函數draw和dispatchDraw的實現如下所示:

public void draw(Canvas canvas) {
    if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
        // draw() is not called when SKIP_DRAW is set
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
            // 繪製黑色背景
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
    }
    super.draw(canvas);
}

@Override
protected void dispatchDraw(Canvas canvas) {
    if (mWindowType != WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
        // if SKIP_DRAW is cleared, draw() has already punched a hole
        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
            // punch a whole in the view-hierarchy below us
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
    }
    super.dispatchDraw(canvas);
}
// 繪製之前調用updateWindow
private final ViewTreeObserver.OnPreDrawListener mDrawListener =
    new ViewTreeObserver.OnPreDrawListener() {
    @Override
    public boolean onPreDraw() {
        // reposition ourselves where the surface is
        mHaveFrame = getWidth() > 0 && getHeight() > 0;
        // 更新View
        updateWindow(false, false);
        return true;
    }
};

updateWindow的主要作用是SurfaceView更新的處理,包括創建mWindow,通知WindowMangerService中Surface的創建,還有Surface的創建,更新,銷燬的通知回調等,主要代碼如下所示:

protected void updateWindow(boolean force, boolean redrawNeeded) {
    if (!mHaveFrame) {
        return;
    }
    ViewRootImpl viewRoot = getViewRootImpl();
    if (viewRoot != null) {
        mTranslator = viewRoot.mTranslator;
    }

    if (mTranslator != null) {
        mSurface.setCompatibilityTranslator(mTranslator);
    }

    int myWidth = mRequestedWidth;
    if (myWidth <= 0) myWidth = getWidth();
    int myHeight = mRequestedHeight;
    if (myHeight <= 0) myHeight = getHeight();

    getLocationInWindow(mLocation);
    final boolean creating = mWindow == null;
    final boolean formatChanged = mFormat != mRequestedFormat;
    final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
    final boolean visibleChanged = mVisible != mRequestedVisible;
    final boolean layoutSizeChanged = getWidth() != mLayout.width || getHeight() != mLayout.height;
    // 判斷SurfaceView是否有發生變化,需要更新
    if (force || creating || formatChanged || sizeChanged || visibleChanged
        || mLeft != mLocation[0] || mTop != mLocation[1]
        || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded || layoutSizeChanged) {

        try {
            ......
            // 更新一下layout,寬高之類的信息
            ......

            if (mWindow == null) {
                Display display = getDisplay();
                //MyWindow是一個Binder類,用於WindowMangerService通知回調SurfaceView,這裏創建MyWindow對象mWIndow,
                //並通過mSession.addToDisplayWithoutInputChannel將這些參數傳給WindowMangerService,
                //通知它爲SurfaceView創建一塊不接收輸入事件的Surface,以便後面繪圖所用。
                mWindow = new MyWindow(this);
                mLayout.type = mWindowType;
                mLayout.gravity = Gravity.START|Gravity.TOP;
                mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
                        mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
                        mStableInsets);
            }

            boolean realSizeChanged;
            boolean reportDrawNeeded;

            int relayoutResult;
            //此處需要用鎖鎖住,防止其他線程同時修改
            mSurfaceLock.lock();
            try {
                mUpdateWindowNeeded = false;
                reportDrawNeeded = mReportDrawNeeded;
                mReportDrawNeeded = false;
                mDrawingStopped = !visible;

                if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
                //通過mSession這個binder對象請求WindowMangerService爲SurfaceView的UI進行佈局,
                //然後WindowMangerService就會填充mNewSurface這個Surface對象,以便後面可以通過它獲取畫布Canvas,進行繪圖操作。
                relayoutResult = mSession.relayout(
                    mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                        visible ? VISIBLE : GONE,
                        WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                        mWinFrame, mOverscanInsets, mContentInsets,
                        mVisibleInsets, mStableInsets, mOutsets, mConfiguration,
                        mNewSurface);
                if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                    reportDrawNeeded = true;
                }


                mSurfaceFrame.left = 0;
                mSurfaceFrame.top = 0;
                if (mTranslator == null) {
                    mSurfaceFrame.right = mWinFrame.width();
                    mSurfaceFrame.bottom = mWinFrame.height();
                } else {
                    float appInvertedScale = mTranslator.applicationInvertedScale;
                    mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
                    mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
                }

                final int surfaceWidth = mSurfaceFrame.right;
                final int surfaceHeight = mSurfaceFrame.bottom;
                realSizeChanged = mLastSurfaceWidth != surfaceWidth
                        || mLastSurfaceHeight != surfaceHeight;
                mLastSurfaceWidth = surfaceWidth;
                mLastSurfaceHeight = surfaceHeight;
            } finally {
                mSurfaceLock.unlock();
            }

            try {
                redrawNeeded |= creating | reportDrawNeeded;

                SurfaceHolder.Callback callbacks[] = null;

                final boolean surfaceChanged = (relayoutResult
                        & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
                if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                    mSurfaceCreated = false;
                    if (mSurface.isValid()) {
                        if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
                        callbacks = getSurfaceCallbacks();
                        // Surface被銷燬,回調實現了SurfaceHolder.Callback的對象
                        for (SurfaceHolder.Callback c : callbacks) {
                            c.surfaceDestroyed(mSurfaceHolder);
                        }
                    }
                }
                // 這裏將最新狀態的mNewSuface對象的數據更新到當前的mSurface中
                mSurface.transferFrom(mNewSurface);

                if (visible && mSurface.isValid()) {
                    if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                        mSurfaceCreated = true;
                        mIsCreating = true;
                        if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
                        if (callbacks == null) {
                            callbacks = getSurfaceCallbacks();
                        }
                        // Surface被創建,回調實現了SurfaceHolder.Callback的對象
                        for (SurfaceHolder.Callback c : callbacks) {
                            c.surfaceCreated(mSurfaceHolder);
                        }
                    }
                    if (creating || formatChanged || sizeChanged
                            || visibleChanged || realSizeChanged) {
                        if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
                                + " w=" + myWidth + " h=" + myHeight);
                        if (callbacks == null) {
                            callbacks = getSurfaceCallbacks();
                        }
                        // Surface變化,回調實現了SurfaceHolder.Callback的對象
                        for (SurfaceHolder.Callback c : callbacks) {
                            c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                        }
                    }
                    if (redrawNeeded) {
                        if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
                        if (callbacks == null) {
                            callbacks = getSurfaceCallbacks();
                        }
                        for (SurfaceHolder.Callback c : callbacks) {
                            if (c instanceof SurfaceHolder.Callback2) {
                                ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                        mSurfaceHolder);
                            }
                        }
                    }
                }
            } finally {
                mIsCreating = false;
                if (redrawNeeded) {
                    if (DEBUG) Log.i(TAG, "finishedDrawing");
                    mSession.finishDrawing(mWindow);
                }
                mSession.performDeferredDestroy(mWindow);
            }
        } catch (RemoteException ex) {
        }
    }
}

從SurfaceView類的成員函數draw和dispatchDraw的實現就可以看出,SurfaceView在其宿主窗口的繪圖表面上面所做的操作就是將自己所佔據的區域繪爲黑色,除此之外,就沒有其它更多的操作了,這是因爲SurfaceView的UI是要展現在它自己的surface上面的,接下來分析如何在SurfaceView的surface上面進行UI繪製。
如果要在一個繪圖表面進行UI繪製,那麼就順序執行以下的操作:

  1. 在繪圖表面的基礎上建立一塊畫布,即獲得一個Canvas對象。
  2. 利用Canvas類提供的繪圖接口在前面獲得的畫布上繪製任意的UI。
  3. 將已經填充好了UI數據的畫布緩衝區提交給SurfaceFlinger服務,以便SurfaceFlinger服務可以將它合成到屏幕上去。
    SurfaceView提供了一個SurfaceHolder接口,通過這個SurfaceHolder接口就可以執行上述操作
private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
    @Override
    public Canvas lockCanvas() {
        return internalLockCanvas(null);
    }
    
    private final Canvas internalLockCanvas(Rect dirty) {
        mSurfaceLock.lock();

        if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
                + mDrawingStopped + ", win=" + mWindow);

        Canvas c = null;
        if (!mDrawingStopped && mWindow != null) {
            try {
                c = mSurface.lockCanvas(dirty);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Exception locking surface", e);
            }
        }

        if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
        if (c != null) {
            mLastLockTime = SystemClock.uptimeMillis();
            return c;
        }

        // If the Surface is not ready to be drawn, then return null,
        // but throttle calls to this function so it isn't called more
        // than every 100ms.
        long now = SystemClock.uptimeMillis();
        long nextTime = mLastLockTime + 100;
        if (nextTime > now) {
            try {
                Thread.sleep(nextTime-now);
            } catch (InterruptedException e) {
            }
            now = SystemClock.uptimeMillis();
        }
        mLastLockTime = now;
        mSurfaceLock.unlock();

        return null;
    }

    @Override
    public void unlockCanvasAndPost(Canvas canvas) {
        mSurface.unlockCanvasAndPost(canvas);
        mSurfaceLock.unlock();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章