前言
上一篇文章和大家论述了SurfaceView的核心原理,本文和大家聊聊TextureView的核心原理。
正文
TextureView的用法这里稍微解释一下。用一个官方的demo看看。
public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener {
private Camera mCamera;
private TextureView mTextureView;
SurfaceTexture.OnFrameAvailableListener mFrameAvailableListener;
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 {
surface.setOnFrameAvailableListener(mFrameAvailableListener);
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
}
}
这是一个最简单的相机预览功能模型。首先通过setSurfaceTextureListener监听TextureView的生命周期:
1.onSurfaceTextureAvailable(TextureView可用).
2.onSurfaceTextureSizeChanged TextureView大小发生了变化
3.onSurfaceTextureUpdated TextureView有视图数据进行了更新。
4.onSurfaceTextureDestroyed TextureView销毁了。
5.setOnFrameAvailableListener 监听TextureView绘制每一帧结束后的回调。
在每一个生命周期中,分别处理了Camera的对应的行为。详细的就不铺开说了。我们主要来关注TextureView,在整个View的绘制流程中做了什么?
TextureView 源码解析
老规矩,我们根据上一篇文章总结的,如何阅读View源码方式进行解析。本文将着重的解析TextureView中lockCanvas,unlockCanvasAndPost,以及draw的方法。
TextureView onMeasure,onLayout,onDraw
TextureView 在 onMeasure,onLayout两个流程并没有什么可说的。我们来看看draw方法。
@Override
public final void draw(Canvas canvas) {
// NOTE: Maintain this carefully (see View#draw)
mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
if (canvas.isHardwareAccelerated()) {
DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
TextureLayer layer = getTextureLayer();
if (layer != null) {
applyUpdate();
applyTransformMatrix();
mLayer.setLayerPaint(mLayerPaint); // ensure layer paint is up to date
displayListCanvas.drawTextureLayer(layer);
}
}
}
和SurfaceView不同,TextureView确实是跟着ViewRootImpl 绘制View 树时候的三大流程走。不过有一点不同的是,ViewRootImpl的draw流程时候,都是绘制在从ViewRootImpl中生成的Canvas中。但是TextureView并直接没有使用从上层传递下来的Canvas,而是通过TextureLayer绘制的。
第二点不同的是,TextureView想要可以正常绘制,当前Activity必须要打开硬件渲染。因为这里面必须使用硬件渲染独有的Canvas DisplayListCanvas进行绘制。否则标志位没进来,TextureView跳过draw的方法就是背景色。
让我们来看看TextureLayer是什么吧。
TextureLayer TextureView的画笔的初始化
TextureLayer getTextureLayer() {
if (mLayer == null) {
if (mAttachInfo == null || mAttachInfo.mThreadedRenderer == null) {
return null;
}
mLayer = mAttachInfo.mThreadedRenderer.createTextureLayer();
boolean createNewSurface = (mSurface == null);
if (createNewSurface) {
// Create a new SurfaceTexture for the layer.
mSurface = new SurfaceTexture(false);
nCreateNativeWindow(mSurface);
}
mLayer.setSurfaceTexture(mSurface);
mSurface.setDefaultBufferSize(getWidth(), getHeight());
mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
if (mListener != null && createNewSurface) {
mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
}
mLayer.setLayerPaint(mLayerPaint);
}
if (mUpdateSurface) {
// Someone has requested that we use a specific SurfaceTexture, so
// tell mLayer about it and set the SurfaceTexture to use the
// current view size.
mUpdateSurface = false;
// Since we are updating the layer, force an update to ensure its
// parameters are correct (width, height, transform, etc.)
updateLayer();
mMatrixChanged = true;
mLayer.setSurfaceTexture(mSurface);
mSurface.setDefaultBufferSize(getWidth(), getHeight());
}
return mLayer;
}
一旦发现mLayer没有初始化,这里做的事情分为如下几个步骤:
1.通过从ViewRootImpl传下来的AttchInfo中的ThreadedRenderer调用createTextureLayer()进行生成一个TextureLayer。ThreadedRenderer这个类是硬件渲染的核心类,所有的硬件渲染都是从这个类开始触发。
2.检查是否创建了SurfaceTexture,没有创建则先创建后调用native方法nCreateNativeWindow 保存起来。并保存到TextureLayer。
3.设置SurfaceTexture的宽高,setOnFrameAvailableListener监听SurfaceTexture的回调,并且回调第一个生命周期onSurfaceTextureAvailable。
4.如果需要更新SurfaceTexture,调用updateLayer更新标志位后,更新TextureLayer中的SurfaceTexture以及重新设置宽高。
这里面涉及了两个比较关键的函数ThreadedRenderer.createTextureLayer以及nCreateNativeWindow,以及一个对象SurfaceTexture。从名字上来看叫做Surface纹理,感觉绘制内容都在里面。我们先来看看SurfaceTexture究竟是什么东西?
SurfaceTexture的初始化
public SurfaceTexture(boolean singleBufferMode) {
mCreatorLooper = Looper.myLooper();
mIsSingleBuffered = singleBufferMode;
nativeInit(true, 0, singleBufferMode, new WeakReference(this));
}
关键是调用nativeInit在native层进行初始化。默认是设置的mIsSingleBuffered为false,mCreatorLooper则为当前线程对应的Looper。所以我们想要创建一个SurfaceTexture,可以不是当前的ui主线程,但是当前线程必须存在初始化好的Looper。
SurfaceTexture native层的初始化
static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached,
jint texName, jboolean singleBufferMode, jobject weakThiz)
{
sp producer;
sp consumer;
BufferQueue::createBufferQueue(&producer, &consumer);
if (singleBufferMode) {
consumer->setMaxBufferCount(1);
}
sp surfaceTexture;
if (isDetached) {
surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES,
true, !singleBufferMode);
} else {
surfaceTexture = new GLConsumer(consumer, texName,
GL_TEXTURE_EXTERNAL_OES, true, !singleBufferMode);
}
if (surfaceTexture == 0) {
jniThrowException(env, OutOfResourcesException,
"Unable to create native SurfaceTexture");
return;
}
surfaceTexture->setName(String8::format("SurfaceTexture-%d-%d-%d",
(isDetached ? 0 : texName),
getpid(),
createProcessUniqueId()));
// If the current context is protected, inform the producer.
consumer->setConsumerIsProtected(isProtectedContext());
SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture);
SurfaceTexture_setProducer(env, thiz, producer);
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
jniThrowRuntimeException(env,
"Can't find android/graphics/SurfaceTexture");
return;
}
sp ctx(new JNISurfaceTextureContext(env, weakThiz,
clazz));
surfaceTexture->setFrameAvailableListener(ctx);
SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
}
首先通过createBufferQueue初始化了两个极其重要的角色:
1.IGraphicBufferProducer 图元生产者
2.IGraphicBufferConsumer 图元消费者
接着,使用初始化好的图元消费者,在SurfaceTexture在native层中初始化了一个极其重要的角色GLConsumer。当然在SurfaceTexture这里面图元生产者和图元消费者要区别于SF进程的图元生产者和图元消费者。具体有什么区别稍后再来聊聊。
最后把IGraphicBufferProducer和GLConsumer以及FrameAvailableListener的地址设置在SurfaceTexture的long类型中。
private long mSurfaceTexture;
private long mProducer;
private long mFrameAvailableListener;
GLConsumer的初始化
GLConsumer::GLConsumer(const sp& bq, uint32_t tex,
uint32_t texTarget, bool useFenceSync, bool isControlledByApp) :
ConsumerBase(bq, isControlledByApp),
mCurrentCrop(Rect::EMPTY_RECT),
mCurrentTransform(0),
mCurrentScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mCurrentFence(Fence::NO_FENCE),
mCurrentTimestamp(0),
mCurrentDataSpace(HAL_DATASPACE_UNKNOWN),
mCurrentFrameNumber(0),
mDefaultWidth(1),
mDefaultHeight(1),
mFilteringEnabled(true),
mTexName(tex),
mUseFenceSync(useFenceSync),
mTexTarget(texTarget),
mEglDisplay(EGL_NO_DISPLAY),
mEglContext(EGL_NO_CONTEXT),
mCurrentTexture(BufferQueue::INVALID_BUFFER_SLOT),
mAttached(true)
{
GLC_LOGV("GLConsumer");
memcpy(mCurrentTransformMatrix, mtxIdentity.asArray(),
sizeof(mCurrentTransformMatrix));
mConsumer->setConsumerUsageBits(DEFAULT_USAGE_FLAGS);
}
GLConsumer的构造函数,我们能看到有这么几个参数:
1.consumer IGraphicBufferConsumer图元消费者
2.texName 一个int型,实际上设置一个纹理id。
3.GL_TEXTURE_EXTERNAL_OES 这是指渲染在屏幕上的纹理类型,就像OpenGL es的GL_TEXTURE_2D这种类型一样。这也是为什么,我们自定义TextureView的渲染纹理时候,往往需要GL_TEXTURE_EXTERNAL_OES这种类型进行承载。
4.useFenceSync 一个boolean型,他的作用是是否使用fence同步栅,一般都为true。
5.isControlledByApp 在这里面被singleBufferMode所控制。singleBufferMode为true的时候,也就是isControlledByApp为false:代表这个GLConsumer最大只能分配一个图元给SurfaceTexture。不过这里一般为false,代表isControlledByApp为true,可以根据需求获取更多的图元。
当然在初始化GLConsumer过程中,分为2种方式一种是detach一种是非detach。这两个有什么区别呢?最主要的区别就是可以设置texName纹理id。因为OpenGL es的纹理是跟着线程的OpenGL的上下文走的。因此,在TextureView在不同线程渲染同一个SurfaceTexture,需要进行一次detach,重新绑定一次当前线程新的纹理。
了解了SurfaceTexture内部其实控制着自己内部的图元生产者和消费者,我们继续看看TextureLayer是什么东西。
ThreadedRenderer.createTextureLayer
TextureLayer createTextureLayer() {
long layer = nCreateTextureLayer(mNativeProxy);
return TextureLayer.adoptTextureLayer(this, layer);
}
在native层创建了一个对应的TextureLayer之后,保存在java层中返回。
native层创建TextureLayer
static jlong android_view_ThreadedRenderer_createTextureLayer(JNIEnv* env, jobject clazz,
jlong proxyPtr) {
RenderProxy* proxy = reinterpret_cast(proxyPtr);
DeferredLayerUpdater* layer = proxy->createTextureLayer();
return reinterpret_cast(layer);
}
RenderProxy这个对象是ThreadedRenderer在初始化的时候,根据RootNode(可以看成硬件绘制的根节点,硬件绘制将会绘制每一个View中的