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中的特性也无法使用。

image.png

从上图不难难看出,通过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。

image.png
  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服务为其设置一个总的透明区域。


image.png
  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();
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335