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轴的层级是最低的
- 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 {
}