Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的區(qū)別
SurfaceView, GLSurfaceView, SurfaceTexture以及TextureView是Android當(dāng)中名字比較繞,關(guān)系又比較密切的幾個(gè)類(lèi)。本文基于Android 5.0(Lollipop)的代碼理一下它們的基本原理,聯(lián)系與區(qū)別。
SurfaceView
SurfaceView從Android 1.0(API level 1)時(shí)就有 。它繼承自類(lèi)View,因此它本質(zhì)上是一個(gè)View。但與普通View不同的是,它有自己的Surface。我們知道,一般的Activity包含的多個(gè)View會(huì)組成View hierachy的樹(shù)形結(jié)構(gòu),只有最頂層的DecorView,也就是根結(jié)點(diǎn)視圖,才是對(duì)WMS可見(jiàn)的。這個(gè)DecorView在WMS中有一個(gè)對(duì)應(yīng)的WindowState。相應(yīng)地,在SF中對(duì)應(yīng)的Layer。而SurfaceView自帶一個(gè)Surface,這個(gè)Surface在WMS中有自己對(duì)應(yīng)的WindowState,在SF中也會(huì)有自己的Layer。如下圖所示:
也就是說(shuō),雖然在App端它仍在View hierachy中,但在Server端(WMS和SF)中,它與宿主窗口是分離的。這樣的好處是對(duì)這個(gè)Surface的渲染可以放到單獨(dú)線程去做,渲染時(shí)可以有自己的GL context。這對(duì)于一些游戲、視頻等性能相關(guān)的應(yīng)用非常有益,因?yàn)樗粫?huì)影響主線程對(duì)事件的響應(yīng)。但它也有缺點(diǎn),因?yàn)檫@個(gè)Surface不在View hierachy中,它的顯示也不受View的屬性控制,所以不能進(jìn)行平移,縮放等變換,也不能放在其它ViewGroup中,一些View中的特性也無(wú)法使用。
GLSurfaceView
GLSurfaceView從Android 1.5(API level 3)開(kāi)始加入,作為SurfaceView的補(bǔ)充。它可以看作是SurfaceView的一種典型使用模式。在SurfaceView的基礎(chǔ)上,它加入了EGL的管理,并自帶了渲染線程。另外它定義了用戶需要實(shí)現(xiàn)的Render接口,提供了用Strategy pattern更改具體Render行為的靈活性。作為GLSurfaceView的Client,只需要將實(shí)現(xiàn)了渲染函數(shù)的Renderer的實(shí)現(xiàn)類(lèi)設(shè)置給GLSurfaceView即可。如:
public class TriangleActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { mGLView = new GLSurfaceView(this); mGLView.setRenderer(new RendererImpl(this));
相關(guān)類(lèi)圖如下。其中SurfaceView中的SurfaceHolder主要是提供了一坨操作Surface的接口。GLSurfaceView中的EglHelper和GLThread分別實(shí)現(xiàn)了上面提到的管理EGL環(huán)境和渲染線程的工作。GLSurfaceView的使用者需要實(shí)現(xiàn)Renderer接口。
SurfaceTexture
SurfaceTexture從Android 3.0(API level 11)加入。和SurfaceView不同的是,它對(duì)圖像流的處理并不直接顯示,而是轉(zhuǎn)為GL外部紋理,因此可用于圖像流數(shù)據(jù)的二次處理(如Camera濾鏡,桌面特效等)。比如Camera的預(yù)覽數(shù)據(jù),變成紋理后可以交給GLSurfaceView直接顯示,也可以通過(guò)SurfaceTexture交給TextureView作為View heirachy中的一個(gè)硬件加速層來(lái)顯示。首先,SurfaceTexture從圖像流(來(lái)自Camera預(yù)覽,視頻解碼,GL繪制場(chǎng)景等)中獲得幀數(shù)據(jù),當(dāng)調(diào)用updateTexImage()時(shí),根據(jù)內(nèi)容流中最近的圖像更新SurfaceTexture對(duì)應(yīng)的GL紋理對(duì)象,接下來(lái),就可以像操作普通GL紋理一樣操作它了。從下面的類(lèi)圖中可以看出,它核心管理著一個(gè)BufferQueue的Consumer和Producer兩端。Producer端用于內(nèi)容流的源輸出數(shù)據(jù),Consumer端用于拿GraphicBuffer并生成紋理。SurfaceTexture.OnFrameAvailableListener用于讓SurfaceTexture的使用者知道有新數(shù)據(jù)到來(lái)。JNISurfaceTextureContext是OnFrameAvailableListener從Native到Java的JNI跳板。其中SurfaceTexture中的attachToGLContext()和detachToGLContext()可以讓多個(gè)GL context共享同一個(gè)內(nèi)容源。
Android 5.0中將BufferQueue的核心部分分離出來(lái),放在BufferQueueCore這個(gè)類(lèi)中。BufferQueueProducer和BufferQueueConsumer分別是它的生產(chǎn)者和消費(fèi)者實(shí)現(xiàn)基類(lèi)(分別實(shí)現(xiàn)了IGraphicBufferProducer和IGraphicBufferConsumer接口)。它們都是由BufferQueue的靜態(tài)函數(shù)createBufferQueue()來(lái)創(chuàng)建的。Surface是生產(chǎn)者端的實(shí)現(xiàn)類(lèi),提供dequeueBuffer/queueBuffer等硬件渲染接口,和lockCanvas/unlockCanvasAndPost等軟件渲染接口,使內(nèi)容流的源可以往BufferQueue中填graphic buffer。GLConsumer繼承自ConsumerBase,是消費(fèi)者端的實(shí)現(xiàn)類(lèi)。它在基類(lèi)的基礎(chǔ)上添加了GL相關(guān)的操作,如將graphic buffer中的內(nèi)容轉(zhuǎn)為GL紋理等操作。到此,以SurfaceTexture為中心的一個(gè)pipeline大體是這樣的:
TextureView
TextureView在4.0(API level 14)中引入。它可以將內(nèi)容流直接投影到View中,可以用于實(shí)現(xiàn)Live preview等功能。和SurfaceView不同,它不會(huì)在WMS中單獨(dú)創(chuàng)建窗口,而是作為View hierachy中的一個(gè)普通View,因此可以和其它普通View一樣進(jìn)行移動(dòng),旋轉(zhuǎn),縮放,動(dòng)畫(huà)等變化。值得注意的是TextureView必須在硬件加速的窗口中。它顯示的內(nèi)容流數(shù)據(jù)可以來(lái)自App進(jìn)程或是遠(yuǎn)端進(jìn)程。從類(lèi)圖中可以看到,TextureView繼承自View,它與其它的View一樣在View hierachy中管理與繪制。TextureView重載了draw()方法,其中主要把SurfaceTexture中收到的圖像數(shù)據(jù)作為紋理更新到對(duì)應(yīng)的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用于通知TextureView內(nèi)容流有新圖像到來(lái)。SurfaceTextureListener接口用于讓TextureView的使用者知道SurfaceTexture已準(zhǔn)備好,這樣就可以把SurfaceTexture交給相應(yīng)的內(nèi)容源。Surface為BufferQueue的Producer接口實(shí)現(xiàn)類(lèi),使生產(chǎn)者可以通過(guò)它的軟件或硬件渲染接口為SurfaceTexture內(nèi)部的BufferQueue提供graphic buffer。
實(shí)例解讀
下面以VideoDumpView.java(位于/frameworks/base/media/tests/MediaDump/src/com/android/mediadump/)為例分析下SurfaceTexture的使用。這個(gè)例子的效果是從MediaPlayer中拿到視頻幀,然后顯示在屏幕上,接著把屏幕上的內(nèi)容dump到指定文件中。因?yàn)镾urfaceTexture本身只產(chǎn)生紋理,所以這里還需要GLSurfaceView配合來(lái)做最后的渲染輸出。
首先,VideoDumpView是GLSurfaceView的繼承類(lèi)。在構(gòu)造函數(shù)VideoDumpView()中會(huì)創(chuàng)建VideoDumpRenderer,也就是GLSurfaceView.Renderer的實(shí)例,然后調(diào)setRenderer()將之設(shè)成GLSurfaceView的Renderer。
109 public VideoDumpView(Context context) { ... 116 mRenderer = new VideoDumpRenderer(context); 117 setRenderer(mRenderer); 118 }
隨后,GLSurfaceView中的GLThread啟動(dòng),創(chuàng)建EGL環(huán)境后回調(diào)VideoDumpRenderer中的onSurfaceCreated()。
519 public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { ... 551 // Create our texture. This has to be done each time the surface is created. 552 int[] textures = new int[1]; 553 GLES20.glGenTextures(1, textures, 0); 554 555 mTextureID = textures[0]; 556 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID); ... 575 mSurface = new SurfaceTexture(mTextureID); 576 mSurface.setOnFrameAvailableListener(this); 577 578 Surface surface = new Surface(mSurface); 579 mMediaPlayer.setSurface(surface);
這里,首先通過(guò)GLES創(chuàng)建GL的外部紋理。外部紋理說(shuō)明它的真正內(nèi)容是放在ion分配出來(lái)的系統(tǒng)物理內(nèi)存中,而不是GPU中,GPU中只是維護(hù)了其元數(shù)據(jù)。接著根據(jù)前面創(chuàng)建的GL紋理對(duì)象創(chuàng)建SurfaceTexture。流程如下:
SurfaceTexture的參數(shù)為GLES接口函數(shù)glGenTexture()得到的紋理對(duì)象id。在初始化函數(shù)SurfaceTexture_init()中,先創(chuàng)建GLConsumer和相應(yīng)的BufferQueue,再將它們的指針通過(guò)JNI放到SurfaceTexture的Java層對(duì)象成員中。
230static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jboolean isDetached, 231 jint texName, jboolean singleBufferMode, jobject weakThiz) 232{ ... 235 BufferQueue::createBufferQueue(&producer, &consumer); ... 242 sp<GLConsumer> surfaceTexture; 243 if (isDetached) { 244 surfaceTexture = new GLConsumer(consumer, GL_TEXTURE_EXTERNAL_OES, 245 true, true); 246 } else { 247 surfaceTexture = new GLConsumer(consumer, texName, 248 GL_TEXTURE_EXTERNAL_OES, true, true); 249 } ... 256 SurfaceTexture_setSurfaceTexture(env, thiz, surfaceTexture); 257 SurfaceTexture_setProducer(env, thiz, producer); ... 266 sp<JNISurfaceTextureContext> ctx(new JNISurfaceTextureContext(env, weakThiz, 267 clazz)); 268 surfaceTexture->setFrameAvailableListener(ctx); 269 SurfaceTexture_setFrameAvailableListener(env, thiz, ctx);
由于直接的Listener在Java層,而觸發(fā)者在Native層,因此需要從Native層回調(diào)到Java層。這里通過(guò)JNISurfaceTextureContext當(dāng)了跳板。JNISurfaceTextureContext的onFrameAvailable()起到了Native和Java的橋接作用:
180void JNISurfaceTextureContext::onFrameAvailable() ... 184 env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);
其中的fields.postEvent早在SurfaceTexture_classInit()中被初始化為SurfaceTexture的postEventFromNative()函數(shù)。這個(gè)函數(shù)往所在線程的消息隊(duì)列中放入消息,異步調(diào)用VideoDumpRenderer的onFrameAvailable()函數(shù),通知VideoDumpRenderer有新的數(shù)據(jù)到來(lái)。 回到onSurfaceCreated(),接下來(lái)創(chuàng)建供外部生產(chǎn)者使用的Surface類(lèi)。Surface的構(gòu)造函數(shù)之一帶有參數(shù)SurfaceTexture。
133 public Surface(SurfaceTexture surfaceTexture) { ... 140 setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
它實(shí)際上是把SurfaceTexture中創(chuàng)建的BufferQueue的Producer接口實(shí)現(xiàn)類(lèi)拿出來(lái)后創(chuàng)建了相應(yīng)的Surface類(lèi)。
135static jlong nativeCreateFromSurfaceTexture(JNIEnv* env, jclass clazz, 136 jobject surfaceTextureObj) { 137 sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(env, surfaceTextureObj)); ... 144 sp<Surface> surface(new Surface(producer, true));
這樣,Surface為BufferQueue的Producer端,SurfaceTexture中的GLConsumer為BufferQueue的Consumer端。當(dāng)通過(guò)Surface繪制時(shí),SurfaceTexture可以通過(guò)updateTexImage()來(lái)將繪制結(jié)果綁定到GL的紋理中。 回到onSurfaceCreated()函數(shù),接下來(lái)調(diào)用setOnFrameAvailableListener()函數(shù)將VideoDumpRenderer(實(shí)現(xiàn)SurfaceTexture.OnFrameAvailableListener接口)作為SurfaceTexture的Listener,因?yàn)樗O(jiān)聽(tīng)內(nèi)容流上是否有新數(shù)據(jù)。接著將SurfaceTexture傳給MediaPlayer,因?yàn)檫@里MediaPlayer是生產(chǎn)者,SurfaceTexture是消費(fèi)者。后者要接收前者輸出的Video frame。這樣,就通過(guò)Observer pattern建立起了一條通知鏈:MediaPlayer -> SurfaceTexture -> VideDumpRenderer。在onFrameAvailable()回調(diào)函數(shù)中,將updateSurface標(biāo)志設(shè)為true,表示有新的圖像到來(lái),需要更新Surface了。為毛不在這兒馬上更新紋理呢,因?yàn)楫?dāng)前可能不在渲染線程。SurfaceTexture對(duì)象可以在任意線程被創(chuàng)建(回調(diào)也會(huì)在該線程被調(diào)用),但updateTexImage()只能在含有紋理對(duì)象的GL context所在線程中被調(diào)用。因此一般情況下回調(diào)中不能直接調(diào)用updateTexImage()。 與此同時(shí),GLSurfaceView中的GLThread也在運(yùn)行,它會(huì)調(diào)用到VideoDumpRenderer的繪制函數(shù)onDrawFrame()。
372 public void onDrawFrame(GL10 glUnused) { ... 377 if (updateSurface) { ... 380 mSurface.updateTexImage(); 381 mSurface.getTransformMatrix(mSTMatrix); 382 updateSurface = false; ... 394 // Activate the texture. 395 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 396 GLES20.glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTextureID); ... 421 // Draw a rectangle and render the video frame as a texture on it. 422 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); ... 429 DumpToFile(frameNumber);
這里,通過(guò)SurfaceTexture的updateTexImage()將內(nèi)容流中的新圖像轉(zhuǎn)成GL中的紋理,再進(jìn)行坐標(biāo)轉(zhuǎn)換。綁定剛生成的紋理,畫(huà)到屏幕上。整個(gè)流程如下:
最后onDrawFrame()調(diào)用DumpToFile()將屏幕上的內(nèi)容倒到文件中。在DumpToFile()中,先用glReadPixels()從屏幕中把像素?cái)?shù)據(jù)存到Buffer中,然后用FileOutputStream輸出到文件。 上面講了SurfaceTexture,下面看看TextureView是如何工作的。還是從例子著手,Android的關(guān)于TextureView的官方文檔(http://developer.android.com/reference/android/view/TextureView.html)給了一個(gè)簡(jiǎn)潔的例子LiveCameraActivity。它它可以將Camera中的內(nèi)容放在View中進(jìn)行顯示。在onCreate()函數(shù)中首先創(chuàng)建TextureView,再將Activity(實(shí)現(xiàn)了TextureView.SurfaceTextureListener接口)傳給TextureView,用于監(jiān)聽(tīng)SurfaceTexture準(zhǔn)備好的信號(hào)。
protected void onCreate(Bundle savedInstanceState) { ... mTextureView = new TextureView(this); mTextureView.setSurfaceTextureListener(this); ... }
TextureView的構(gòu)造函數(shù)并不做主要的初始化工作。主要的初始化工作是在getHardwareLayer()中,而這個(gè)函數(shù)是在其基類(lèi)View的draw()中調(diào)用。TextureView重載了這個(gè)函數(shù):
348 HardwareLayer getHardwareLayer() { ... 358 mLayer = mAttachInfo.mHardwareRenderer.createTextureLayer(); 359 if (!mUpdateSurface) { 360 // Create a new SurfaceTexture for the layer. 361 mSurface = new SurfaceTexture(false); 362 mLayer.setSurfaceTexture(mSurface); 363 } 364 mSurface.setDefaultBufferSize(getWidth(), getHeight()); 365 nCreateNativeWindow(mSurface); 366 367 mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); 368 369 if (mListener != null && !mUpdateSurface) { 370 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); 371 } ... 390 applyUpdate(); 391 applyTransformMatrix(); 392 393 return mLayer; 394 }
因?yàn)門(mén)extureView是硬件加速層(類(lèi)型為L(zhǎng)AYER_TYPE_HARDWARE),它首先通過(guò)HardwareRenderer創(chuàng)建相應(yīng)的HardwareLayer類(lèi),放在mLayer成員中。然后創(chuàng)建SurfaceTexture類(lèi),具體流程見(jiàn)前文。之后將HardwareLayer與SurfaceTexture做綁定。接著調(diào)用Native函數(shù)nCreateNativeWindow,它通過(guò)SurfaceTexture中的BufferQueueProducer創(chuàng)建Surface類(lèi)。注意Surface實(shí)現(xiàn)了ANativeWindow接口,這意味著它可以作為EGL Surface傳給EGL接口從而進(jìn)行硬件繪制。然后setOnFrameAvailableListener()將監(jiān)聽(tīng)者mUpdateListener注冊(cè)到SurfaceTexture。這樣,當(dāng)內(nèi)容流上有新的圖像到來(lái),mUpdateListener的onFrameAvailable()就會(huì)被調(diào)用。然后需要調(diào)用注冊(cè)在TextureView中的SurfaceTextureListener的onSurfaceTextureAvailable()回調(diào)函數(shù),通知TextureView的使用者SurfaceTexture已就緒。整個(gè)流程大體如下:
注意這里這里為T(mén)extureView創(chuàng)建了DeferredLayerUpdater,而不是像Android 4.4(Kitkat)中返回GLES20TextureLayer。因?yàn)锳ndroid 5.0(Lollipop)中在App端分離出了渲染線程,并將渲染工作放到該線程中。這個(gè)線程還能接收VSync信號(hào),因此它還能自己處理動(dòng)畫(huà)。事實(shí)上,這里DeferredLayerUpdater的創(chuàng)建就是通過(guò)同步方式在渲染線程中做的。DeferredLayerUpdater,顧名思義,就是將Layer的更新請(qǐng)求先記錄在這,當(dāng)渲染線程真正要畫(huà)的時(shí)候,再進(jìn)行真正的操作。其中的setSurfaceTexture()會(huì)調(diào)用HardwareLayer的Native函數(shù)nSetSurfaceTexture()將SurfaceTexture中的surfaceTexture成員(類(lèi)型為GLConsumer)傳給DeferredLayerUpdater,這樣之后要更新紋理時(shí)DeferredLayerUpdater就知道從哪里更新了。 前面提到初始化中會(huì)調(diào)用onSurfaceTextureAvailable()這個(gè)回調(diào)函數(shù)。在它的實(shí)現(xiàn)中,TextureView的使用者就可以將準(zhǔn)備好的SurfaceTexture傳給數(shù)據(jù)源模塊,供數(shù)據(jù)源輸出之用。如:
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { mCamera = Camera.open(); ... mCamera.setPreviewTexture(surface); mCamera.startPreview(); ... }
看一下setPreviewTexture()的實(shí)現(xiàn),其中把SurfaceTexture中初始化時(shí)創(chuàng)建的GraphicBufferProducer拿出來(lái)傳給Camera模塊。
576static void android_hardware_Camera_setPreviewTexture(JNIEnv *env, 577 jobject thiz, jobject jSurfaceTexture) ... 585 producer = SurfaceTexture_getProducer(env, jSurfaceTexture); ... 594 if (camera->setPreviewTarget(producer) != NO_ERROR) {
到這里,一切都初始化地差不多了。接下來(lái)當(dāng)內(nèi)容流有新圖像可用,TextureView會(huì)被通知到(通過(guò)SurfaceTexture.OnFrameAvailableListener接口)。SurfaceTexture.OnFrameAvailableListener是SurfaceTexture有新內(nèi)容來(lái)時(shí)的回調(diào)接口。TextureView中的mUpdateListener實(shí)現(xiàn)了該接口:
755 public void onFrameAvailable(SurfaceTexture surfaceTexture) { 756 updateLayer(); 757 invalidate(); 758 }
可以看到其中會(huì)調(diào)用updateLayer()函數(shù),然后通過(guò)invalidate()函數(shù)申請(qǐng)更新UI。updateLayer()會(huì)設(shè)置mUpdateLayer標(biāo)志位。這樣,當(dāng)下次VSync到來(lái)時(shí),Choreographer通知App通過(guò)重繪View hierachy。在UI重繪函數(shù)performTranversals()中,作為View hierachy的一分子,TextureView的draw()函數(shù)被調(diào)用,其中便會(huì)相繼調(diào)用applyUpdate()和HardwareLayer的updateSurfaceTexture()函數(shù)。
138 public void updateSurfaceTexture() { 139 nUpdateSurfaceTexture(mFinalizer.get()); 140 mRenderer.pushLayerUpdate(this); 141 }
updateSurfaceTexture()實(shí)際通過(guò)JNI調(diào)用到android_view_HardwareLayer_updateSurfaceTexture()函數(shù)。在其中會(huì)設(shè)置相應(yīng)DeferredLayerUpdater的標(biāo)志位mUpdateTexImage,它表示在渲染線程中需要更新該層的紋理。
前面提到,Android 5.0引入了渲染線程,它是一個(gè)更大的topic,超出本文范圍,這里只說(shuō)相關(guān)的部分。作為背景知識(shí),下面只畫(huà)出了相關(guān)的類(lèi)??梢钥吹?,ThreadedRenderer作為新的HardwareRenderer替代了Android 4.4中的Gl20Renderer。其中比較關(guān)鍵的是RenderProxy類(lèi),需要讓渲染線程干活時(shí)就通過(guò)這個(gè)類(lèi)往渲染線程發(fā)任務(wù)。RenderProxy中指向的RenderThread就是渲染線程的主體了,其中的threadLoop()函數(shù)是主循環(huán),大多數(shù)時(shí)間它會(huì)poll在線程的Looper上等待,當(dāng)有同步請(qǐng)求(或者VSync信號(hào))過(guò)來(lái),它會(huì)被喚醒,然后處理TaskQueue中的任務(wù)。TaskQueue是RenderTask的隊(duì)列,RenderTask代表一個(gè)渲染線程中的任務(wù)。如DrawFrameTask就是RenderTask的繼承類(lèi)之一,它主要用于渲染當(dāng)前幀。而DrawFrameTask中的DeferredLayerUpdater集合就存放著之前對(duì)硬件加速層的更新操作申請(qǐng)。
當(dāng)主線程準(zhǔn)備好渲染數(shù)據(jù)后,會(huì)以同步方式讓渲染線程完成渲染工作。其中會(huì)先調(diào)用processLayerUpdate()更新所有硬件加速層中的屬性,繼而調(diào)用到DeferredLayerUpdater的apply()函數(shù),其中檢測(cè)到標(biāo)志位mUpdateTexImage被置位,于是會(huì)調(diào)用doUpdateTexImage()真正更新GL紋理和轉(zhuǎn)換坐標(biāo)。
最后,總結(jié)下這幾者的區(qū)別和聯(lián)系。簡(jiǎn)單地說(shuō),SurfaceView是一個(gè)有自己Surface的View。它的渲染可以放在單獨(dú)線程而不是主線程中。其缺點(diǎn)是不能做變形和動(dòng)畫(huà)。SurfaceTexture可以用作非直接輸出的內(nèi)容流,這樣就提供二次處理的機(jī)會(huì)。與SurfaceView直接輸出相比,這樣會(huì)有若干幀的延遲。同時(shí),由于它本身管理BufferQueue,因此內(nèi)存消耗也會(huì)稍微大一些。TextureView是一個(gè)可以把內(nèi)容流作為外部紋理輸出在上面的View。它本身需要是一個(gè)硬件加速層。事實(shí)上TextureView本身也包含了SurfaceTexture。它與SurfaceView+SurfaceTexture組合相比可以完成類(lèi)似的功能(即把內(nèi)容流上的圖像轉(zhuǎn)成紋理,然后輸出)。區(qū)別在于TextureView是在View hierachy中做繪制,因此一般它是在主線程上做的(在Android 5.0引入渲染線程后,它是在渲染線程中做的)。而SurfaceView+SurfaceTexture在單獨(dú)的Surface上做繪制,可以是用戶提供的線程,而不是系統(tǒng)的主線程或是渲染線程。另外,與TextureView相比,它還有個(gè)好處是可以用Hardware overlay進(jìn)行顯示。
到此這篇關(guān)于Android中SurfaceTexture TextureView SurfaceView GLSurfaceView的區(qū)別的文章就介紹到這了,更多相關(guān)Android SurfaceTexture TextureView SurfaceView GLSurfaceView內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android Lottie實(shí)現(xiàn)中秋月餅變明月動(dòng)畫(huà)特效實(shí)例
Lottie是Airbnb開(kāi)源的一個(gè)支持 Android、iOS 以及 ReactNative,利用json文件的方式快速實(shí)現(xiàn)動(dòng)畫(huà)效果的庫(kù),下面這篇文章主要給大家介紹了關(guān)于Android Lottie實(shí)現(xiàn)中秋月餅變明月動(dòng)畫(huà)特效的相關(guān)資料,需要的朋友可以參考下2021-09-09Android中使用tcpdump、wireshark進(jìn)行抓包并分析技術(shù)介紹
這篇文章主要介紹了Android中使用tcpdump、wireshark進(jìn)行抓包并分析技術(shù)介紹,本文講解了下載并安裝tcpdump、pc上安裝wireshark等內(nèi)容,需要的朋友可以參考下2015-04-04淺談Android中關(guān)于靜態(tài)變量(static)的使用問(wèn)題
本文主要介紹了Android中關(guān)于靜態(tài)變量(static)的使用問(wèn)題,具有一定的參考作用,下面跟著小編一起來(lái)看下吧2017-01-01Android HttpURLConnection.getResponseCode()錯(cuò)誤解決方法
在使用HttpURLConnection.getResponseCode()的時(shí)候直接報(bào)錯(cuò)是IOException錯(cuò)誤,一直想不明白,同一個(gè)程序我調(diào)用了兩次,結(jié)果有一個(gè)鏈接一直O(jiān)K,另一個(gè)卻一直報(bào)這個(gè)錯(cuò)誤2013-06-06Android入門(mén)之使用SharedPreference存取信息詳解
這篇文章主要為大家詳細(xì)介紹了Android如何使用SharedPreference實(shí)現(xiàn)存取信息,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定的幫助,需要的可以參考一下2022-12-12移動(dòng)端android上line-height不居中的問(wèn)題的解決
現(xiàn)在越來(lái)越多的移動(dòng)界面使用rem適配,最近發(fā)現(xiàn)了移動(dòng)端android上line-height不居中的問(wèn)題,今日就來(lái)介紹一下解決的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2018-03-03Android TextView跑馬燈效果實(shí)現(xiàn)方法
這篇文章主要介紹了Android TextView跑馬燈效果實(shí)現(xiàn)方法,涉及Android布局文件中相關(guān)屬性的設(shè)置技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2016-01-01