Android(安卓)5.0(Lollipop)中的SurfaceTexture,TextureView, SurfaceView和GLSurfaceView
16lz
2021-01-24
SurfaceView, GLSurfaceView, SurfaceTexture以及TextureView是Android当中名字比较绕,关系又比较密切的几个类。本文基于Android 5.0(Lollipop)的代码理一下它们的基本原理,联系与区别。
GLSurfaceView从Android 1.5(API level 3)开始加入,作为SurfaceView的补充。它可以看作是SurfaceView的一种典型使用模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程。另外它定义了用户需要实现的Render接口,提供了用Strategy pattern更改具体Render行为的灵活性。作为GLSurfaceView的Client,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。如:
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共享同一个内容源。
Android 5.0中将BufferQueue的核心部分分离出来,放在BufferQueueCore这个类中。BufferQueueProducer和BufferQueueConsumer分别是它的生产者和消费者实现基类(分别实现了IGraphicBufferProducer和IGraphicBufferConsumer接口)。它们都是由BufferQueue的静态函数createBufferQueue()来创建的。Surface是生产者端的实现类,提供dequeueBuffer/queueBuffer等硬件渲染接口,和lockCanvas/unlockCanvasAndPost等软件渲染接口,使内容流的源可以往BufferQueue中填graphic buffer。GLConsumer继承自ConsumerBase,是消费者端的实现类。它在基类的基础上添加了GL相关的操作,如将graphic buffer中的内容转为GL纹理等操作。到此,以SurfaceTexture为中心的一个pipeline大体是这样的:
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。
下面以VideoDumpView.java(位于/frameworks/base/media/tests/MediaDump/src/com/android/mediadump/)为例分析下SurfaceTexture的使用。这个例子的效果是从MediaPlayer中拿到视频帧,然后显示在屏幕上,接着把屏幕上的内容dump到指定文件中。因为SurfaceTexture本身只产生纹理,所以这里还需要GLSurfaceView配合来做最后的渲染输出。
首先,VideoDumpView是GLSurfaceView的继承类。在构造函数VideoDumpView()中会创建VideoDumpRenderer,也就是GLSurfaceView.Renderer的实例,然后调setRenderer()将之设成GLSurfaceView的Renderer。
回到onSurfaceCreated(),接下来创建供外部生产者使用的Surface类。Surface的构造函数之一带有参数SurfaceTexture。
回到onSurfaceCreated()函数,接下来调用setOnFrameAvailableListener()函数将VideoDumpRenderer(实现SurfaceTexture.OnFrameAvailableListener接口)作为SurfaceTexture的Listener,因为它要监听内容流上是否有新数据。接着将SurfaceTexture传给MediaPlayer,因为这里MediaPlayer是生产者,SurfaceTexture是消费者。后者要接收前者输出的Video frame。这样,就通过Observer pattern建立起了一条通知链:MediaPlayer -> SurfaceTexture -> VideDumpRenderer。在onFrameAvailable()回调函数中,将updateSurface标志设为true,表示有新的图像到来,需要更新Surface了。为毛不在这儿马上更新纹理呢,因为当前可能不在渲染线程。SurfaceTexture对象可以在任意线程被创建(回调也会在该线程被调用),但updateTexImage()只能在含有纹理对象的GL context所在线程中被调用。因此一般情况下回调中不能直接调用updateTexImage()。
与此同时,GLSurfaceView中的GLThread也在运行,它会调用到VideoDumpRenderer的绘制函数onDrawFrame()。
最后onDrawFrame()调用DumpToFile()将屏幕上的内容倒到文件中。在DumpToFile()中,先用glReadPixels()从屏幕中把像素数据存到Buffer中,然后用FileOutputStream输出到文件。
上面讲了SurfaceTexture,下面看看TextureView是如何工作的。还是从例子着手,Android的关于TextureView的官方文档(http://developer.android.com/reference/android/view/TextureView.html)给了一个简洁的例子LiveCameraActivity。它它可以将Camera中的内容放在View中进行显示。在onCreate()函数中首先创建TextureView,再将Activity(实现了TextureView.SurfaceTextureListener接口)传给TextureView,用于监听SurfaceTexture准备好的信号。
注意这里这里为TextureView创建了DeferredLayerUpdater,而不是像Android 4.4(Kitkat)中返回GLES20TextureLayer。因为Android 5.0(Lollipop)中在App端分离出了渲染线程,并将渲染工作放到该线程中。这个线程还能接收VSync信号,因此它还能自己处理动画。事实上,这里DeferredLayerUpdater的创建就是通过同步方式在渲染线程中做的。DeferredLayerUpdater,顾名思义,就是将Layer的更新请求先记录在这,当渲染线程真正要画的时候,再进行真正的操作。其中的setSurfaceTexture()会调用HardwareLayer的Native函数nSetSurfaceTexture()将SurfaceTexture中的surfaceTexture成员(类型为GLConsumer)传给DeferredLayerUpdater,这样之后要更新纹理时DeferredLayerUpdater就知道从哪里更新了。
前面提到初始化中会调用onSurfaceTextureAvailable()这个回调函数。在它的实现中,TextureView的使用者就可以将准备好的SurfaceTexture传给数据源模块,供数据源输出之用。如:
前面提到,Android 5.0引入了渲染线程,它是一个更大的topic,超出本文范围,这里只说相关的部分。作为背景知识,下面只画出了相关的类。可以看到,ThreadedRenderer作为新的HardwareRenderer替代了Android 4.4中的Gl20Renderer。其中比较关键的是RenderProxy类,需要让渲染线程干活时就通过这个类往渲染线程发任务。RenderProxy中指向的RenderThread就是渲染线程的主体了,其中的threadLoop()函数是主循环,大多数时间它会poll在线程的Looper上等待,当有同步请求(或者VSync信号)过来,它会被唤醒,然后处理TaskQueue中的任务。TaskQueue是RenderTask的队列,RenderTask代表一个渲染线程中的任务。如DrawFrameTask就是RenderTask的继承类之一,它主要用于渲染当前帧。而DrawFrameTask中的DeferredLayerUpdater集合就存放着之前对硬件加速层的更新操作申请。 当主线程准备好渲染数据后,会以同步方式让渲染线程完成渲染工作。其中会先调用processLayerUpdate()更新所有硬件加速层中的属性,继而调用到DeferredLayerUpdater的apply()函数,其中检测到标志位mUpdateTexImage被置位,于是会调用doUpdateTexImage()真正更新GL纹理和转换坐标。
最后,总结下这几者的区别和联系。简单地说,SurfaceView是一个有自己Surface的View。它的渲染可以放在单独线程而不是主线程中。其缺点是不能做变形和动画。SurfaceTexture可以用作非直接输出的内容流,这样就提供二次处理的机会。与SurfaceView直接输出相比,这样会有若干帧的延迟。同时,由于它本身管理BufferQueue,因此内存消耗也会稍微大一些。TextureView是一个可以把内容流作为外部纹理输出在上面的View。它本身需要是一个硬件加速层。事实上TextureView本身也包含了SurfaceTexture。它与SurfaceView+SurfaceTexture组合相比可以完成类似的功能(即把内容流上的图像转成纹理,然后输出)。区别在于TextureView是在View hierachy中做绘制,因此一般它是在主线程上做的(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。而SurfaceView+SurfaceTexture在单独的Surface上做绘制,可以是用户提供的线程,而不是系统的主线程或是渲染线程。另外,与TextureView相比,它还有个好处是可以用Hardware overlay进行显示。
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。如下图所示:
GLSurfaceView从Android 1.5(API level 3)开始加入,作为SurfaceView的补充。它可以看作是SurfaceView的一种典型使用模式。在SurfaceView的基础上,它加入了EGL的管理,并自带了渲染线程。另外它定义了用户需要实现的Render接口,提供了用Strategy pattern更改具体Render行为的灵活性。作为GLSurfaceView的Client,只需要将实现了渲染函数的Renderer的实现类设置给GLSurfaceView即可。如:
- publicclassTriangleActivityextendsActivity{
- protectedvoidonCreate(BundlesavedInstanceState){
- mGLView=newGLSurfaceView(this);
- mGLView.setRenderer(newRendererImpl(this));
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共享同一个内容源。
Android 5.0中将BufferQueue的核心部分分离出来,放在BufferQueueCore这个类中。BufferQueueProducer和BufferQueueConsumer分别是它的生产者和消费者实现基类(分别实现了IGraphicBufferProducer和IGraphicBufferConsumer接口)。它们都是由BufferQueue的静态函数createBufferQueue()来创建的。Surface是生产者端的实现类,提供dequeueBuffer/queueBuffer等硬件渲染接口,和lockCanvas/unlockCanvasAndPost等软件渲染接口,使内容流的源可以往BufferQueue中填graphic buffer。GLConsumer继承自ConsumerBase,是消费者端的实现类。它在基类的基础上添加了GL相关的操作,如将graphic buffer中的内容转为GL纹理等操作。到此,以SurfaceTexture为中心的一个pipeline大体是这样的:
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。
下面以VideoDumpView.java(位于/frameworks/base/media/tests/MediaDump/src/com/android/mediadump/)为例分析下SurfaceTexture的使用。这个例子的效果是从MediaPlayer中拿到视频帧,然后显示在屏幕上,接着把屏幕上的内容dump到指定文件中。因为SurfaceTexture本身只产生纹理,所以这里还需要GLSurfaceView配合来做最后的渲染输出。
首先,VideoDumpView是GLSurfaceView的继承类。在构造函数VideoDumpView()中会创建VideoDumpRenderer,也就是GLSurfaceView.Renderer的实例,然后调setRenderer()将之设成GLSurfaceView的Renderer。
- 109publicVideoDumpView(Contextcontext){
- ...
- 116mRenderer=newVideoDumpRenderer(context);
- 117setRenderer(mRenderer);
- 118}
- 519publicvoidonSurfaceCreated(GL10glUnused,EGLConfigconfig){
- ...
- 551//Createourtexture.Thishastobedoneeachtimethesurfaceiscreated.
- 552int[]textures=newint[1];
- 553GLES20.glGenTextures(1,textures,0);
- 554
- 555mTextureID=textures[0];
- 556GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES,mTextureID);
- ...
- 575mSurface=newSurfaceTexture(mTextureID);
- 576mSurface.setOnFrameAvailableListener(this);
- 577
- 578Surfacesurface=newSurface(mSurface);
- 579mMediaPlayer.setSurface(surface);
- 230staticvoidSurfaceTexture_init(JNIEnv*env,jobjectthiz,jbooleanisDetached,
- 231jinttexName,jbooleansingleBufferMode,jobjectweakThiz)
- 232{
- ...
- 235BufferQueue::createBufferQueue(&producer,&consumer);
- ...
- 242sp<GLConsumer>surfaceTexture;
- 243if(isDetached){
- 244surfaceTexture=newGLConsumer(consumer,GL_TEXTURE_EXTERNAL_OES,
- 245true,true);
- 246}else{
- 247surfaceTexture=newGLConsumer(consumer,texName,
- 248GL_TEXTURE_EXTERNAL_OES,true,true);
- 249}
- ...
- 256SurfaceTexture_setSurfaceTexture(env,thiz,surfaceTexture);
- 257SurfaceTexture_setProducer(env,thiz,producer);
- ...
- 266sp<JNISurfaceTextureContext>ctx(newJNISurfaceTextureContext(env,weakThiz,
- 267clazz));
- 268surfaceTexture->setFrameAvailableListener(ctx);
- 269SurfaceTexture_setFrameAvailableListener(env,thiz,ctx);
- 180voidJNISurfaceTextureContext::onFrameAvailable()
- ...
- 184env->CallStaticVoidMethod(mClazz,fields.postEvent,mWeakThiz);
回到onSurfaceCreated(),接下来创建供外部生产者使用的Surface类。Surface的构造函数之一带有参数SurfaceTexture。
- 133publicSurface(SurfaceTexturesurfaceTexture){
- ...
- 140setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
- 135staticjlongnativeCreateFromSurfaceTexture(JNIEnv*env,jclassclazz,
- 136jobjectsurfaceTextureObj){
- 137sp<IGraphicBufferProducer>producer(SurfaceTexture_getProducer(env,surfaceTextureObj));
- ...
- 144sp<Surface>surface(newSurface(producer,true));
回到onSurfaceCreated()函数,接下来调用setOnFrameAvailableListener()函数将VideoDumpRenderer(实现SurfaceTexture.OnFrameAvailableListener接口)作为SurfaceTexture的Listener,因为它要监听内容流上是否有新数据。接着将SurfaceTexture传给MediaPlayer,因为这里MediaPlayer是生产者,SurfaceTexture是消费者。后者要接收前者输出的Video frame。这样,就通过Observer pattern建立起了一条通知链:MediaPlayer -> SurfaceTexture -> VideDumpRenderer。在onFrameAvailable()回调函数中,将updateSurface标志设为true,表示有新的图像到来,需要更新Surface了。为毛不在这儿马上更新纹理呢,因为当前可能不在渲染线程。SurfaceTexture对象可以在任意线程被创建(回调也会在该线程被调用),但updateTexImage()只能在含有纹理对象的GL context所在线程中被调用。因此一般情况下回调中不能直接调用updateTexImage()。
与此同时,GLSurfaceView中的GLThread也在运行,它会调用到VideoDumpRenderer的绘制函数onDrawFrame()。
- 372publicvoidonDrawFrame(GL10glUnused){
- ...
- 377if(updateSurface){
- ...
- 380mSurface.updateTexImage();
- 381mSurface.getTransformMatrix(mSTMatrix);
- 382updateSurface=false;
- ...
- 394//Activatethetexture.
- 395GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- 396GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES,mTextureID);
- ...
- 421//Drawarectangleandrenderthevideoframeasatextureonit.
- 422GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);
- ...
- 429DumpToFile(frameNumber);
最后onDrawFrame()调用DumpToFile()将屏幕上的内容倒到文件中。在DumpToFile()中,先用glReadPixels()从屏幕中把像素数据存到Buffer中,然后用FileOutputStream输出到文件。
上面讲了SurfaceTexture,下面看看TextureView是如何工作的。还是从例子着手,Android的关于TextureView的官方文档(http://developer.android.com/reference/android/view/TextureView.html)给了一个简洁的例子LiveCameraActivity。它它可以将Camera中的内容放在View中进行显示。在onCreate()函数中首先创建TextureView,再将Activity(实现了TextureView.SurfaceTextureListener接口)传给TextureView,用于监听SurfaceTexture准备好的信号。
- protectedvoidonCreate(BundlesavedInstanceState){
- ...
- mTextureView=newTextureView(this);
- mTextureView.setSurfaceTextureListener(this);
- ...
- }
- 348HardwareLayergetHardwareLayer(){
- ...
- 358mLayer=mAttachInfo.mHardwareRenderer.createTextureLayer();
- 359if(!mUpdateSurface){
- 360//CreateanewSurfaceTextureforthelayer.
- 361mSurface=newSurfaceTexture(false);
- 362mLayer.setSurfaceTexture(mSurface);
- 363}
- 364mSurface.setDefaultBufferSize(getWidth(),getHeight());
- 365nCreateNativeWindow(mSurface);
- 366
- 367mSurface.setOnFrameAvailableListener(mUpdateListener,mAttachInfo.mHandler);
- 368
- 369if(mListener!=null&&!mUpdateSurface){
- 370mListener.onSurfaceTextureAvailable(mSurface,getWidth(),getHeight());
- 371}
- ...
- 390applyUpdate();
- 391applyTransformMatrix();
- 392
- 393returnmLayer;
- 394}
注意这里这里为TextureView创建了DeferredLayerUpdater,而不是像Android 4.4(Kitkat)中返回GLES20TextureLayer。因为Android 5.0(Lollipop)中在App端分离出了渲染线程,并将渲染工作放到该线程中。这个线程还能接收VSync信号,因此它还能自己处理动画。事实上,这里DeferredLayerUpdater的创建就是通过同步方式在渲染线程中做的。DeferredLayerUpdater,顾名思义,就是将Layer的更新请求先记录在这,当渲染线程真正要画的时候,再进行真正的操作。其中的setSurfaceTexture()会调用HardwareLayer的Native函数nSetSurfaceTexture()将SurfaceTexture中的surfaceTexture成员(类型为GLConsumer)传给DeferredLayerUpdater,这样之后要更新纹理时DeferredLayerUpdater就知道从哪里更新了。
前面提到初始化中会调用onSurfaceTextureAvailable()这个回调函数。在它的实现中,TextureView的使用者就可以将准备好的SurfaceTexture传给数据源模块,供数据源输出之用。如:
- publicvoidonSurfaceTextureAvailable(SurfaceTexturesurface,intwidth,intheight){
- mCamera=Camera.open();
- ...
- mCamera.setPreviewTexture(surface);
- mCamera.startPreview();
- ...
- }
- 576staticvoidandroid_hardware_Camera_setPreviewTexture(JNIEnv*env,
- 577jobjectthiz,jobjectjSurfaceTexture)
- ...
- 585producer=SurfaceTexture_getProducer(env,jSurfaceTexture);
- ...
- 594if(camera->setPreviewTarget(producer)!=NO_ERROR){
- 755publicvoidonFrameAvailable(SurfaceTexturesurfaceTexture){
- 756updateLayer();
- 757invalidate();
- 758}
- 138publicvoidupdateSurfaceTexture(){
- 139nUpdateSurfaceTexture(mFinalizer.get());
- 140mRenderer.pushLayerUpdate(this);
- 141}
前面提到,Android 5.0引入了渲染线程,它是一个更大的topic,超出本文范围,这里只说相关的部分。作为背景知识,下面只画出了相关的类。可以看到,ThreadedRenderer作为新的HardwareRenderer替代了Android 4.4中的Gl20Renderer。其中比较关键的是RenderProxy类,需要让渲染线程干活时就通过这个类往渲染线程发任务。RenderProxy中指向的RenderThread就是渲染线程的主体了,其中的threadLoop()函数是主循环,大多数时间它会poll在线程的Looper上等待,当有同步请求(或者VSync信号)过来,它会被唤醒,然后处理TaskQueue中的任务。TaskQueue是RenderTask的队列,RenderTask代表一个渲染线程中的任务。如DrawFrameTask就是RenderTask的继承类之一,它主要用于渲染当前帧。而DrawFrameTask中的DeferredLayerUpdater集合就存放着之前对硬件加速层的更新操作申请。 当主线程准备好渲染数据后,会以同步方式让渲染线程完成渲染工作。其中会先调用processLayerUpdate()更新所有硬件加速层中的属性,继而调用到DeferredLayerUpdater的apply()函数,其中检测到标志位mUpdateTexImage被置位,于是会调用doUpdateTexImage()真正更新GL纹理和转换坐标。
最后,总结下这几者的区别和联系。简单地说,SurfaceView是一个有自己Surface的View。它的渲染可以放在单独线程而不是主线程中。其缺点是不能做变形和动画。SurfaceTexture可以用作非直接输出的内容流,这样就提供二次处理的机会。与SurfaceView直接输出相比,这样会有若干帧的延迟。同时,由于它本身管理BufferQueue,因此内存消耗也会稍微大一些。TextureView是一个可以把内容流作为外部纹理输出在上面的View。它本身需要是一个硬件加速层。事实上TextureView本身也包含了SurfaceTexture。它与SurfaceView+SurfaceTexture组合相比可以完成类似的功能(即把内容流上的图像转成纹理,然后输出)。区别在于TextureView是在View hierachy中做绘制,因此一般它是在主线程上做的(在Android 5.0引入渲染线程后,它是在渲染线程中做的)。而SurfaceView+SurfaceTexture在单独的Surface上做绘制,可以是用户提供的线程,而不是系统的主线程或是渲染线程。另外,与TextureView相比,它还有个好处是可以用Hardware overlay进行显示。
更多相关文章
- C语言函数的递归(上)
- 深入浅出Android(安卓)Handler
- Handler 消息传递机制介绍
- Android的Looper类使用的5个要点
- android中Bitmap数据如何释放
- Android(安卓)Handler
- Android线程池(一)简单使用
- android耗时任务_IntentService
- android多线程handler+runOnUithread+view.post+handler.post