梳理SurfaceView源码20230125

Surface类

1、类的定义:在Android生态中,无论开发者用什么渲染 API,一切内容都会渲染到 Surface 上

/**
 * Handle onto a raw buffer that is being managed by the screen compositor.
 *
 * <p>A Surface is generally created by or from a consumer of image buffers (such as a
 * {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or
 * {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as
 * {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},
 * {@link android.media.MediaPlayer#setSurface MediaPlayer}, or
 * {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw
 * into.</p>
 *
 * <p><strong>Note:</strong> A Surface acts like a
 * {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By
 * itself it will not keep its parent consumer from being reclaimed.</p>
 */
public class Surface implements Parcelable {
    private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
            throws OutOfResourcesException;

    private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);
    private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject);

    private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)
            throws OutOfResourcesException;
    private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);

    private static native void nativeRelease(long nativeObject);
    private static native boolean nativeIsValid(long nativeObject);
    private static native boolean nativeIsConsumerRunningBehind(long nativeObject);
    private static native long nativeReadFromParcel(long nativeObject, Parcel source);
    private static native void nativeWriteToParcel(long nativeObject, Parcel dest);

    private static native void nativeAllocateBuffers(long nativeObject);

    private static native int nativeGetWidth(long nativeObject);
    private static native int nativeGetHeight(long nativeObject);

    private static native long nativeGetNextFrameNumber(long nativeObject);
    private static native int nativeSetScalingMode(long nativeObject, int scalingMode);
    private static native int nativeForceScopedDisconnect(long nativeObject);
    private static native int nativeAttachAndQueueBuffer(long nativeObject, GraphicBuffer buffer);

    private static native int nativeSetSharedBufferModeEnabled(long nativeObject, boolean enabled);
    private static native int nativeSetAutoRefreshEnabled(long nativeObject, boolean enabled);
}

数据:raw buffer
生产方:OpenGL、MediaPlayer、CameraDevice(相机设备)
消费方:SurfaceTexture、MediaRecorder、Allocation

2、我们在消费方创建一个Surface给生产方,在生产方绘制,然后再Surface的回调里面给消费方

SurfaceView类

主要功能是通过SurfaceHolder来对Surface进行管理

/**
 * Provides a dedicated drawing surface embedded inside of a view hierarchy.
 * You can control the format of this surface and, if you like, its size; the
 * SurfaceView takes care of placing the surface at the correct location on the
 * screen
 *
 * <p>The surface is Z ordered so that it is behind the window holding its
 * SurfaceView; the SurfaceView punches a hole in its window to allow its
 * surface to be displayed. The view hierarchy will take care of correctly
 * compositing with the Surface any siblings of the SurfaceView that would
 * normally appear on top of it. This can be used to place overlays such as
 * buttons on top of the Surface, though note however that it can have an
 * impact on performance since a full alpha-blended composite will be performed
 * each time the Surface changes.
 *
 * <p> The transparent region that makes the surface visible is based on the
 * layout positions in the view hierarchy. If the post-layout transform
 * properties are used to draw a sibling view on top of the SurfaceView, the
 * view may not be properly composited with the surface.
 *
 * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
 * which can be retrieved by calling {@link #getHolder}.
 *
 * <p>The Surface will be created for you while the SurfaceView's window is
 * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
 * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
 * Surface is created and destroyed as the window is shown and hidden.
 *
 * <p>One of the purposes of this class is to provide a surface in which a
 * secondary thread can render into the screen. If you are going to use it
 * this way, you need to be aware of some threading semantics:
 *
 * <ul>
 * <li> All SurfaceView and
 * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
 * from the thread running the SurfaceView's window (typically the main thread
 * of the application). They thus need to correctly synchronize with any
 * state that is also touched by the drawing thread.
 * <li> You must ensure that the drawing thread only touches the underlying
 * Surface while it is valid -- between
 * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
 * and
 * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
 * </ul>
 *
 * <p class="note"><strong>Note:</strong> Starting in platform version
 * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
 * updated synchronously with other View rendering. This means that translating
 * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
 * artifacts may occur on previous versions of the platform when its window is
 * positioned asynchronously.</p>
 */
public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
    final Surface mSurface = new Surface()
    
   /**
     * Return the SurfaceHolder providing access and control over this
     * SurfaceView's underlying surface.
     *
     * @return SurfaceHolder The holder of the surface.
     */
    public SurfaceHolder getHolder() {
        return mSurfaceHolder;
    }

    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {

        @Override
        public boolean isCreating() {
            return false;
        }

        @Override
        public void addCallback(Callback callback) {
        }

        @Override
        public void removeCallback(Callback callback) {
        }

        @Override
        public void setFixedSize(int width, int height) {
        }

        @Override
        public void setSizeFromLayout() {
        }

        @Override
        public void setFormat(int format) {
        }

        @Override
        public void setType(int type) {
        }

        @Override
        public void setKeepScreenOn(boolean screenOn) {
        }

        @Override
        public Canvas lockCanvas() {
            return null;
        }

        @Override
        public Canvas lockCanvas(Rect dirty) {
            return null;
        }

        @Override
        public void unlockCanvasAndPost(Canvas canvas) {
        }

        @Override
        public Surface getSurface() {
            return null;
        }

        @Override
        public Rect getSurfaceFrame() {
            return null;
        }
    };
}

1、SurfaceHolder是一个管理surface的抽象类

/**
 * Abstract interface to someone holding a display surface.  Allows you to
 * control the surface size and format, edit the pixels in the surface, and
 * monitor changes to the surface.  This interface is typically available
 * through the {@link SurfaceView} class.
 *
 * <p>When using this interface from a thread other than the one running
 * its {@link SurfaceView}, you will want to carefully read the
 * methods
 * {@link #lockCanvas} and {@link Callback#surfaceCreated Callback.surfaceCreated()}.
 */
public interface SurfaceHolder {
}

2、updateSurface()方法,里面调用了3个常用的回调

 protected void updateSurface() {
       ...
     for (SurfaceHolder.Callback c : callbacks) {
           c.surfaceDestroyed(mSurfaceHolder);
      }
      ...
     for (SurfaceHolder.Callback c : callbacks) {
           c.surfaceCreated(mSurfaceHolder);
     }
    ...
     for (SurfaceHolder.Callback c : callbacks) {
            c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
     }
}

挖洞?

1、在SurfaceView的onAttachedToWindow()方法中,调用mParent.requestTransparentRegio(),请求父布局给一块空白区域

 SurfaceView类
 @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
         ...
        mParent.requestTransparentRegion(SurfaceView.this);
        ...
    }

实际调用ViewParent类
@Override
public void requestTransparentRegion(View child) {}

ViewRootImpl就是ViewParent类型,我们来到这个类的requestTransparentRegion()方法

public final class ViewRootImpl extends Handler implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
 public void requestTransparentRegion(View child) {
        checkThread();
        if (mView == child) {     
            requestLayout();
        }
    }
    
    public void requestLayout() {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }

    public void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;

            //noinspection ConstantConditions
            if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
                final long now = System.nanoTime();
                Log.d(TAG, "Latency: Scheduled traversal, it has been "
                        + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
                        + "ms since the last traversal finished.");
            }

            sendEmptyMessage(DO_TRAVERSAL);
        }
    }

 @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
         ...
        case DO_TRAVERSAL:    
            ...
            performTraversals();
         }
         ...
    }

    private void performTraversals() {
      ...
      host.gatherTransparentRegion(mTransparentRegion);
      ...
    }
}

回到View的继承类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;
    }

重点

仔细看SurfaceView、Surface类说明

参考:
https://www.itdaan.com/blog/2017/03/22/3f106d8c7a506df6d21665b166d3f393.html
https://blog.csdn.net/luoshengyang/article/details/8661317

  • SurfaceView是在独立的UI线程(非主线程)上绘制的,这样可以应对复杂的绘制,且不会造成ANR,具体参考Surface类的内容
  • SurfaceView会在宿主窗口上挖一个洞显示自己,且它的z轴的层级是最低的


    image.png
  • SurfaceView有独立的绘图canvas(来自Surface,而不是SurfaceView的onDraw方法)
  SurfaceView sv = (SurfaceView )findViewById(R.id.surface_view);
  SurfaceHolder sh = sv.getHolder();
  Cavas canvas = sh.lockCanvas()
  //Draw something on canvas
   sh.unlockCanvasAndPost(canvas);
  • 双缓冲机制,上面一点从 Cavas canvas = sh.lockCanvas()拿到的Cavas就是该机制的实现。用两个Cavas交替取值
    在运用时可以理解为:SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。

其他

关于TextureView

TextureView 在 API 14 中引入,用来展示流,比如视频和 OpenGL 等的流。这些流可以来自应用进程或者是跨进程的。它只能用在开启了硬件加速的窗口,否则无法绘制任何内容。与 SurefaceView 不同,TextureView 不会创建一个独立的窗口,而是像一个普通的 View 一样。这种区别使得 TextureView 可以移动、转换和做动画等,比如你可以使用 TextureView 的 setAlpha() 方法将其设置成半透明的

读类注释
1、可以展示在同一进程,不同进程的视频流
2、只能在硬件加速的window
3、没有创建新的window(和surfaceview不一样)
/**
 * <p>A TextureView can be used to display a content stream. Such a content
 * stream can for instance be a video or an OpenGL scene. The content stream
 * can come from the application's process as well as a remote process.</p>
 *
 * <p>TextureView can only be used in a hardware accelerated window. When
 * rendered in software, TextureView will draw nothing.</p>
 *
 * <p>Unlike {@link SurfaceView}, TextureView does not create a separate
 * window but behaves as a regular View. This key difference allows a
 * TextureView to be moved, transformed, animated, etc. For instance, you
 * can make a TextureView semi-translucent by calling
 * <code>myView.setAlpha(0.5f)</code>.</p>
 *
 * <p>Using a TextureView is simple: all you need to do is get its
 * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to
 * render content. The following example demonstrates how to render the
 * camera preview into a TextureView:</p>
 *
 * <pre>
 *  public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
 *      private Camera mCamera;
 *      private TextureView mTextureView;
 *
 *      protected void onCreate(Bundle savedInstanceState) {
 *          super.onCreate(savedInstanceState);
 *
 *          mTextureView = new TextureView(this);
 *          mTextureView.setSurfaceTextureListener(this);
 *
 *          setContentView(mTextureView);
 *      }
 *
 *      public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
 *          mCamera = Camera.open();
 *
 *          try {
 *              mCamera.setPreviewTexture(surface);
 *              mCamera.startPreview();
 *          } catch (IOException ioe) {
 *              // Something bad happened
 *          }
 *      }
 *
 *      public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
 *          // Ignored, Camera does all the work for us
 *      }
 *
 *      public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
 *          mCamera.stopPreview();
 *          mCamera.release();
 *          return true;
 *      }
 *
 *      public void onSurfaceTextureUpdated(SurfaceTexture surface) {
 *          // Invoked every time there's a new Camera preview frame
 *      }
 *  }
 * </pre>
 *
 * <p>A TextureView's SurfaceTexture can be obtained either by invoking
 * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}.
 * It is important to know that a SurfaceTexture is available only after the
 * TextureView is attached to a window (and {@link #onAttachedToWindow()} has
 * been invoked.) It is therefore highly recommended you use a listener to
 * be notified when the SurfaceTexture becomes available.</p>
 *
 * <p>It is important to note that only one producer can use the TextureView.
 * For instance, if you use a TextureView to display the camera preview, you
 * cannot use {@link #lockCanvas()} to draw onto the TextureView at the same
 * time.</p>
 *
 * @see SurfaceView
 * @see SurfaceTexture
 */
public class TextureView extends View {
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容