Android 5.0(Lollipop)中的SurfaceTexture装诡,TextureView, SurfaceView和GLSurfaceView

學(xué)習(xí)筆記版
原文轉(zhuǎn)自: http://blog.csdn.net/jinzhuojun/article/details/44062175
SurfaceView, GLSurfaceView, SurfaceTexture以及TextureView是Android當(dāng)中名字比較繞,關(guān)系又比較密切的幾個類。本文基于Android 5.0(Lollipop)的代碼理一下它們的基本原理嚼酝,聯(lián)系與區(qū)別。

SurfaceView

SurfaceView從Android 1.0(API level 1)時就有 竟坛。它繼承自類View闽巩,因此它本質(zhì)上是一個View。但與普通View不同的是担汤,它有自己的Surface涎跨。我們知道,一般的Activity包含的多個View會組成View hierachy的樹形結(jié)構(gòu)崭歧,只有最頂層的DecorView六敬,也就是根結(jié)點視圖,才是對WMS可見的驾荣。這個DecorView在WMS中有一個對應(yīng)的WindowState。相應(yīng)地普泡,在SurfaceFlinger(SF)中對應(yīng)的Layer播掷。而SurfaceView自帶一個Surface,這個Surface在WMS中有自己對應(yīng)的WindowState撼班,在SurfaceFlinger中也會有自己的Layer歧匈。如下圖所示:


圖片.png

也就是說,雖然在App端它仍在View hierachy中砰嘁,但在Server端(WMS和SF)中件炉,它與宿主窗口是分離的勘究。這樣的好處是對這個Surface的渲染可以放到單獨線程去做,渲染時可以有自己的GL context斟冕。這對于一些游戲口糕、視頻等性能相關(guān)的應(yīng)用非常有益,因為它不會影響主線程對事件的響應(yīng)磕蛇。但它也有缺點景描,因為這個Surface不在View hierachy中,它的顯示也不受View的屬性控制秀撇,所以不能進行平移超棺,縮放等變換,也不能放在其它ViewGroup中呵燕,一些View中的特性也無法使用棠绘。

備注:

SurfaceView可以自定義子類, 繼承于SurfaceView就可以了.
使用的時候可以直接new 一個不帶參數(shù)的構(gòu)造函數(shù)的對象, 或者隨意定義.
比如這樣:

public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

在Activity中直接使用:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    int type = getIntent().getIntExtra("type", 0);
    if (type == 0) {
        setContentView(new MySurfaceView(this));

也可以在xml中定義, 那么自定義類里面就必須實現(xiàn)三個參數(shù)的那個構(gòu)造方法.
這么看來其實還是比較靈活的
自定義SurfaceView中如果要動態(tài)的更新顯示, 就得啟動一個線程來做.
線程啟動的最好的時機就是在surfaceCreated()回調(diào)方法中.
在這個線程中畫圖時, 通過SurfaceHolder來獲取Canvas對象, 而且用完之后必須unlock.

canvas = surfaceHolder.lockCanvas(); //獲取Canvas對象
surfaceHolder.unlockCanvasAndPost(canvas); // 釋放Canvas對象.
SurfaceView也是直接繼承自View的, 也有onTouchEvent等方法, 所以除了觸摸事件之類的不是問題
onMeasure(), onLayout(), onSizeChanged(), onWindowVisibilityChanged(), onAttachedToWindow() 這些方法都會被回調(diào), 而且都是在UI線程.
但是 onDraw(),onFinishInflate()方法都 不會被回調(diào), 這個需要注意!

GLSurfaceView

GLSurfaceView從Android 1.5(API level 3)開始加入,作為SurfaceView的補充再扭。它可以看作是SurfaceView的一種典型使用模式氧苍。在SurfaceView的基礎(chǔ)上,它加入了EGL的管理霍衫,并自帶了渲染線程候引。另外它定義了用戶需要實現(xiàn)的Render接口,提供了用Strategy pattern更改具體Render行為的靈活性敦跌。作為GLSurfaceView的Client澄干,只需要將實現(xiàn)了渲染函數(shù)的Renderer的實現(xiàn)類設(shè)置給GLSurfaceView即可。如:

public class TriangleActivity extends Activity {  
    protected void onCreate(Bundle savedInstanceState) {  
        mGLView = new GLSurfaceView(this);  
        mGLView.setRenderer(new RendererImpl(this));  

相關(guān)類圖如下柠傍。其中SurfaceView中的SurfaceHolder主要是提供了一些操作Surface的接口麸俘。GLSurfaceView中的EglHelper和GLThread分別實現(xiàn)了上面提到的管理EGL環(huán)境和渲染線程的工作。GLSurfaceView的使用者需要實現(xiàn)Renderer接口惧笛。


圖片.png

SurfaceTexture

SurfaceTexture從Android 3.0(API level 11)加入从媚。和SurfaceView不同的是,它對圖像流的處理并不直接顯示患整,而是轉(zhuǎn)為GL外部紋理拜效,因此可用于圖像流數(shù)據(jù)的二次處理(如Camera濾鏡,桌面特效等)各谚。比如Camera的預(yù)覽數(shù)據(jù)紧憾,變成紋理后可以交給GLSurfaceView直接顯示,也可以通過SurfaceTexture交給TextureView作為View heirachy中的一個硬件加速層來顯示昌渤。首先赴穗,SurfaceTexture從圖像流(來自Camera預(yù)覽,視頻解碼,GL繪制場景等)中獲得幀數(shù)據(jù)般眉,當(dāng)調(diào)用updateTexImage()時了赵,根據(jù)內(nèi)容流中最近的圖像更新SurfaceTexture對應(yīng)的GL紋理對象,接下來甸赃,就可以像操作普通GL紋理一樣操作它了柿汛。從下面的類圖中可以看出腌闯,它核心管理著一個BufferQueue的Consumer和Producer兩端晴弃。Producer端用于內(nèi)容流的源輸出數(shù)據(jù),Consumer端用于獲取GraphicBuffer并生成紋理族操。SurfaceTexture.OnFrameAvailableListener用于讓SurfaceTexture的使用者知道有新數(shù)據(jù)到來鸠窗。JNISurfaceTextureContext是OnFrameAvailableListener從Native到Java的JNI跳板妓羊。其中SurfaceTexture中的attachToGLContext()和detachToGLContext()可以讓多個GL context共享同一個內(nèi)容源。
上面這段讀了幾次也沒能理解具體的含義, 看來還是得真正通過代碼來理解

圖片.png

Android 5.0中將BufferQueue的核心部分分離出來稍计,放在BufferQueueCore這個類中躁绸。BufferQueueProducer和BufferQueueConsumer分別是它的生產(chǎn)者和消費者實現(xiàn)基類(分別實現(xiàn)了IGraphicBufferProducer和IGraphicBufferConsumer接口)。它們都是由BufferQueue的靜態(tài)函數(shù)createBufferQueue()來創(chuàng)建的臣嚣。Surface是生產(chǎn)者端的實現(xiàn)類净刮,提供dequeueBuffer/queueBuffer等硬件渲染接口,和lockCanvas/unlockCanvasAndPost等軟件渲染接口硅则,使內(nèi)容流的源可以往BufferQueue中填graphic buffer淹父。GLConsumer繼承自ConsumerBase,是消費者端的實現(xiàn)類怎虫。它在基類的基礎(chǔ)上添加了GL相關(guān)的操作暑认,如將graphic buffer中的內(nèi)容轉(zhuǎn)為GL紋理等操作。到此大审,以SurfaceTexture為中心的一個pipeline大體是這樣的:

圖片.png

以上仍然看不懂

TextureView在4.0(API level 14)中引入蘸际。它可以將內(nèi)容流直接投影到View中,可以用于實現(xiàn)Live preview等功能徒扶。和SurfaceView不同粮彤,它不會在WMS中單獨創(chuàng)建窗口,而是作為View hierachy中的一個普通View姜骡,因此可以和其它普通View一樣進行移動导坟,旋轉(zhuǎn),縮放圈澈,動畫等變化惫周。值得注意的是TextureView必須在硬件加速的窗口中。它顯示的內(nèi)容流數(shù)據(jù)可以來自App進程或是遠端進程士败。從類圖中可以看到,TextureView繼承自View,它與其它的View一樣在View hierachy中管理與繪制谅将。TextureView重載了draw()方法漾狼,其中主要把SurfaceTexture中收到的圖像數(shù)據(jù)作為紋理更新到對應(yīng)的HardwareLayer中。SurfaceTexture.OnFrameAvailableListener用于通知TextureView內(nèi)容流有新圖像到來饥臂。SurfaceTextureListener接口用于讓TextureView的使用者比如MediaPlayer?知道SurfaceTexture已準(zhǔn)備好逊躁,這樣就可以把SurfaceTexture交給相應(yīng)的內(nèi)容源。Surface為BufferQueue的Producer接口實現(xiàn)類隅熙,使生產(chǎn)者可以通過它的軟件或硬件渲染接口為SurfaceTexture內(nèi)部的BufferQueue提供graphic buffer稽煤。

圖片.png

感覺TextureView需要配合SurfaceTexture來一起使用

下面以VideoDumpView.java(位于/frameworks/base/media/tests/MediaDump/src/com/android/mediadump/)為例分析下SurfaceTexture的使用。這個例子的效果是從MediaPlayer中拿到視頻幀囚戚,然后顯示在屏幕上酵熙,接著把屏幕上的內(nèi)容dump到指定文件中。因為SurfaceTexture本身只產(chǎn)生紋理驰坊,所以這里還需要GLSurfaceView配合來做最后的渲染輸出匾二。

首先,VideoDumpView是GLSurfaceView的繼承類拳芙。在構(gòu)造函數(shù)VideoDumpView()中會創(chuàng)建VideoDumpRenderer察藐,也就是GLSurfaceView.Renderer的實例,然后調(diào)setRenderer()將之設(shè)成GLSurfaceView的Renderer舟扎。

109    public VideoDumpView(Context context) {  
...  
116        mRenderer = new VideoDumpRenderer(context);  
117        setRenderer(mRenderer);  
118    }  

隨后分飞,GLSurfaceView中的GLThread啟動,創(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);  

這里譬猫,首先通過GLES創(chuàng)建GL的外部紋理。外部紋理說明它的真正內(nèi)容是放在ion分配出來的系統(tǒng)物理內(nèi)存中邦泄,而不是GPU中删窒,GPU中只是維護了其元數(shù)據(jù)。接著根據(jù)前面創(chuàng)建的GL紋理對象創(chuàng)建SurfaceTexture顺囊。流程如下:


圖片.png

SurfaceTexture的參數(shù)為GLES接口函數(shù)glGenTexture()得到的紋理對象id肌索。在初始化函數(shù)SurfaceTexture_init()中,先創(chuàng)建GLConsumer和相應(yīng)的BufferQueue特碳,再將它們的指針通過JNI放到SurfaceTexture的Java層對象成員中诚亚。

230 static 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層站宗。這里通過JNISurfaceTextureContext當(dāng)了跳板。JNISurfaceTextureContext的onFrameAvailable()起到了Native和Java的橋接作用:

180void JNISurfaceTextureContext::onFrameAvailable()  
...  
184        env->CallStaticVoidMethod(mClazz, fields.postEvent, mWeakThiz);  

其中的fields.postEvent早在SurfaceTexture_classInit()中被初始化為SurfaceTexture的postEventFromNative()函數(shù)益愈。這個函數(shù)往所在線程的消息隊列中放入消息梢灭,異步調(diào)用VideoDumpRenderer的onFrameAvailable()函數(shù)夷家,通知VideoDumpRenderer有新的數(shù)據(jù)到來。

回到onSurfaceCreated()敏释,接下來創(chuàng)建供外部生產(chǎn)者使用的Surface類库快。Surface的構(gòu)造函數(shù)之一帶有參數(shù)SurfaceTexture。

133    public Surface(SurfaceTexture surfaceTexture) {  
...  
140            setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));  

它實際上是把SurfaceTexture中創(chuàng)建的BufferQueue的Producer接口實現(xiàn)類拿出來后創(chuàng)建了相應(yīng)的Surface類钥顽。

135 static 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)通過Surface繪制時闽铐,SurfaceTexture可以通過updateTexImage()來將繪制結(jié)果綁定到GL的紋理中。

回到onSurfaceCreated()函數(shù)奶浦,接下來調(diào)用setOnFrameAvailableListener()函數(shù)將VideoDumpRenderer(實現(xiàn)SurfaceTexture.OnFrameAvailableListener接口)作為SurfaceTexture的Listener兄墅,因為它要監(jiān)聽內(nèi)容流上是否有新數(shù)據(jù)。接著將SurfaceTexture傳給MediaPlayer财喳,因為這里MediaPlayer是生產(chǎn)者察迟,SurfaceTexture是消費者。后者要接收前者輸出的Video frame耳高。這樣扎瓶,就通過Observer pattern建立起了一條通知鏈:MediaPlayer -> SurfaceTexture -> VideDumpRenderer。在onFrameAvailable()回調(diào)函數(shù)中泌枪,將updateSurface標(biāo)志設(shè)為true概荷,表示有新的圖像到來,需要更新Surface了碌燕。為毛不在這兒馬上更新紋理呢误证,因為當(dāng)前可能不在渲染線程。SurfaceTexture對象可以在任意線程被創(chuàng)建(回調(diào)也會在該線程被調(diào)用)修壕,但updateTexImage()只能在含有紋理對象的GL context所在線程中被調(diào)用愈捅。因此一般情況下回調(diào)中不能直接調(diào)用updateTexImage()。

與此同時慈鸠,GLSurfaceView中的GLThread也在運行蓝谨,它會調(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);  

這里青团,通過SurfaceTexture的updateTexImage()將內(nèi)容流中的新圖像轉(zhuǎn)成GL中的紋理譬巫,再進行坐標(biāo)轉(zhuǎn)換。綁定剛生成的紋理督笆,畫到屏幕上芦昔。整個流程如下:

最后onDrawFrame()調(diào)用DumpToFile()將屏幕上的內(nèi)容倒到文件中。在DumpToFile()中娃肿,先用glReadPixels()從屏幕中把像素數(shù)據(jù)存到Buffer中咕缎,然后用FileOutputStream輸出到文件珠十。

上面講了SurfaceTexture,下面看看TextureView是如何工作的凭豪。還是從例子著手宵睦,Android的關(guān)于TextureView的官方文檔(http://developer.android.com/reference/android/view/TextureView.html)給了一個簡潔的例子LiveCameraActivity。它它可以將Camera中的內(nèi)容放在View中進行顯示墅诡。在onCreate()函數(shù)中首先創(chuàng)建TextureView,再將Activity(實現(xiàn)了TextureView.SurfaceTextureListener接口)傳給TextureView桐智,用于監(jiān)聽SurfaceTexture準(zhǔn)備好的信號末早。

protected void onCreate(Bundle savedInstanceState) {  
    ...  
    mTextureView = new TextureView(this);  
    mTextureView.setSurfaceTextureListener(this);  
    ...  
}  

TextureView的構(gòu)造函數(shù)并不做主要的初始化工作。主要的初始化工作是在getHardwareLayer()中说庭,而這個函數(shù)是在其基類View的draw()中調(diào)用然磷。TextureView重載了這個函數(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    }  

因為TextureView是硬件加速層(類型為LAYER_TYPE_HARDWARE),它首先通過HardwareRenderer創(chuàng)建相應(yīng)的HardwareLayer類刊驴,放在mLayer成員中姿搜。然后創(chuàng)建SurfaceTexture類,具體流程見前文捆憎。之后將HardwareLayer與SurfaceTexture做綁定舅柜。接著調(diào)用Native函數(shù)nCreateNativeWindow,它通過SurfaceTexture中的BufferQueueProducer創(chuàng)建Surface類躲惰。注意Surface實現(xiàn)了ANativeWindow接口致份,這意味著它可以作為EGL Surface傳給EGL接口從而進行硬件繪制。然后setOnFrameAvailableListener()將監(jiān)聽者mUpdateListener注冊到SurfaceTexture础拨。這樣氮块,當(dāng)內(nèi)容流上有新的圖像到來,mUpdateListener的onFrameAvailable()就會被調(diào)用诡宗。然后需要調(diào)用注冊在TextureView中的SurfaceTextureListener的onSurfaceTextureAvailable()回調(diào)函數(shù)滔蝉,通知TextureView的使用者SurfaceTexture已就緒。整個流程大體如下:

注意這里這里為TextureView創(chuàng)建了DeferredLayerUpdater塔沃,而不是像Android 4.4(Kitkat)中返回GLES20TextureLayer蝠引。因為Android 5.0(Lollipop)中在App端分離出了渲染線程,并將渲染工作放到該線程中芳悲。這個線程還能接收VSync信號立肘,因此它還能自己處理動畫。事實上名扛,這里DeferredLayerUpdater的創(chuàng)建就是通過同步方式在渲染線程中做的谅年。DeferredLayerUpdater,顧名思義肮韧,就是將Layer的更新請求先記錄在這融蹂,當(dāng)渲染線程真正要畫的時候旺订,再進行真正的操作。其中的setSurfaceTexture()會調(diào)用HardwareLayer的Native函數(shù)nSetSurfaceTexture()將SurfaceTexture中的surfaceTexture成員(類型為GLConsumer)傳給DeferredLayerUpdater超燃,這樣之后要更新紋理時DeferredLayerUpdater就知道從哪里更新了区拳。

前面提到初始化中會調(diào)用onSurfaceTextureAvailable()這個回調(diào)函數(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()的實現(xiàn)届良,其中把SurfaceTexture中初始化時創(chuàng)建的GraphicBufferProducer拿出來傳給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) {  

到這里,一切都初始化地差不多了士葫。接下來當(dāng)內(nèi)容流有新圖像可用乞而,TextureView會被通知到(通過SurfaceTexture.OnFrameAvailableListener接口)。SurfaceTexture.OnFrameAvailableListener是SurfaceTexture有新內(nèi)容來時的回調(diào)接口慢显。TextureView中的mUpdateListener實現(xiàn)了該接口:

755        public void onFrameAvailable(SurfaceTexture surfaceTexture) {  
756            updateLayer();  
757            invalidate();  
758        }  

可以看到其中會調(diào)用updateLayer()函數(shù)爪模,然后通過invalidate()函數(shù)申請更新UI。updateLayer()會設(shè)置mUpdateLayer標(biāo)志位荚藻。這樣屋灌,當(dāng)下次VSync到來時,Choreographer通知App通過重繪View hierachy应狱。在UI重繪函數(shù)performTranversals()中声滥,作為View hierachy的一分子,TextureView的draw()函數(shù)被調(diào)用侦香,其中便會相繼調(diào)用applyUpdate()和HardwareLayer的updateSurfaceTexture()函數(shù)落塑。

138    public void updateSurfaceTexture() {  
139        nUpdateSurfaceTexture(mFinalizer.get());  
140        mRenderer.pushLayerUpdate(this);  
141    }  

updateSurfaceTexture()實際通過JNI調(diào)用到android_view_HardwareLayer_updateSurfaceTexture()函數(shù)。在其中會設(shè)置相應(yīng)DeferredLayerUpdater的標(biāo)志位mUpdateTexImage罐韩,它表示在渲染線程中需要更新該層的紋理憾赁。

前面提到,Android 5.0引入了渲染線程散吵,它是一個更大的topic龙考,超出本文范圍,這里只說相關(guān)的部分矾睦。作為背景知識晦款,下面只畫出了相關(guān)的類∶度撸可以看到缓溅,ThreadedRenderer作為新的HardwareRenderer替代了Android 4.4中的Gl20Renderer。其中比較關(guān)鍵的是RenderProxy類赁温,需要讓渲染線程干活時就通過這個類往渲染線程發(fā)任務(wù)坛怪。RenderProxy中指向的RenderThread就是渲染線程的主體了淤齐,其中的threadLoop()函數(shù)是主循環(huán),大多數(shù)時間它會poll在線程的Looper上等待袜匿,當(dāng)有同步請求(或者VSync信號)過來更啄,它會被喚醒,然后處理TaskQueue中的任務(wù)居灯。TaskQueue是RenderTask的隊列祭务,RenderTask代表一個渲染線程中的任務(wù)。如DrawFrameTask就是RenderTask的繼承類之一怪嫌,它主要用于渲染當(dāng)前幀待牵。而DrawFrameTask中的DeferredLayerUpdater集合就存放著之前對硬件加速層的更新操作申請。

當(dāng)主線程準(zhǔn)備好渲染數(shù)據(jù)后喇勋,會以同步方式讓渲染線程完成渲染工作。其中會先調(diào)用processLayerUpdate()更新所有硬件加速層中的屬性偎行,繼而調(diào)用到DeferredLayerUpdater的apply()函數(shù)川背,其中檢測到標(biāo)志位mUpdateTexImage被置位,于是會調(diào)用doUpdateTexImage()真正更新GL紋理和轉(zhuǎn)換坐標(biāo)蛤袒。

最后熄云,總結(jié)下這幾者的區(qū)別和聯(lián)系。簡單地說妙真,SurfaceView是一個有自己Surface的View缴允。它的渲染可以放在單獨線程而不是主線程中。其缺點是不能做變形和動畫(這里說的變形和動畫應(yīng)該指的是View整體的變形和動畫, 因為SurfaceView內(nèi)是完全可以實現(xiàn)動態(tài)的圖像變化的)珍德。SurfaceTexture可以用作非直接輸出的內(nèi)容流练般,這樣就提供二次處理的機會。與SurfaceView直接輸出(直接顯示出來)相比锈候,這樣會有若干幀的延遲薄料。同時,由于它本身管理BufferQueue泵琳,因此內(nèi)存消耗也會稍微大一些摄职。TextureView是一個可以把內(nèi)容流作為外部紋理輸出在上面的View。它本身需要是一個硬件加速層获列。事實上TextureView本身也包含了SurfaceTexture谷市。它與SurfaceView+SurfaceTexture組合相比可以完成類似的功能(即把內(nèi)容流上的圖像轉(zhuǎn)成紋理,然后輸出)击孩。區(qū)別在于TextureView是在View hierachy中做繪制迫悠,因此一般它是在主線程上做的(在Android 5.0引入渲染線程后,它是在渲染線程中做的)巩梢。而SurfaceView+SurfaceTexture在單獨的Surface上做繪制及皂,可以是用戶提供的線程甫男,而不是系統(tǒng)的主線程或是渲染線程。另外验烧,與TextureView相比板驳,它還有個好處是可以用Hardware overlay進行顯示。
什么是hardware overlay?
基本上就是說SurfaceView+SurfaceTexture可以組合在一起, TextureView和SurfaceTexture也可以組合在一起, 這兩種組合一個是在自定義線程中, 一個是個主線程(5.0之后也不是主線程而是渲染 線程)中

SurfaceView, GLSurfaceView, SurfaceTexture以及TextureView是Android當(dāng)中名字比較繞碍拆,關(guān)系又比較密切的幾個類若治。本文基于Android 5.0(Lollipop)的代碼理一下它們的基本原理,聯(lián)系與區(qū)別感混。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末端幼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子弧满,更是在濱河造成了極大的恐慌婆跑,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庭呜,死亡現(xiàn)場離奇詭異滑进,居然都是意外死亡,警方通過查閱死者的電腦和手機募谎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門扶关,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人数冬,你說我怎么就攤上這事节槐。” “怎么了拐纱?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵铜异,是天一觀的道長。 經(jīng)常有香客問我秸架,道長熙掺,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任咕宿,我火速辦了婚禮币绩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘府阀。我一直安慰自己缆镣,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布试浙。 她就那樣靜靜地躺著董瞻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上钠糊,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天挟秤,我揣著相機與錄音,去河邊找鬼抄伍。 笑死艘刚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的截珍。 我是一名探鬼主播攀甚,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岗喉!你這毒婦竟也來了秋度?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钱床,失蹤者是張志新(化名)和其女友劉穎荚斯,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體查牌,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡事期,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了僧免。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡捏浊,死狀恐怖懂衩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情金踪,我是刑警寧澤浊洞,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站胡岔,受9級特大地震影響法希,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜靶瘸,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一苫亦、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怨咪,春花似錦屋剑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匠楚,卻和暖如春巍膘,著一層夾襖步出監(jiān)牢的瞬間厂财,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工峡懈, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留璃饱,地道東北人。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓逮诲,卻偏偏與公主長得像帜平,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梅鹦,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容