图1 TextureView及其依赖的java/c++类
本文将从TetureView的用途、使用模式及其在Framework依赖的类(图1所示那些)的三个方面进行说明。
1. 用途
TextureView可用于承载显示『数据流』的场合,之前看到『流』不太明确其意义,这里给两个具体的场景大家体会一下:camera模块从sensor采集了1080p@30fps的预览数据『流』,视频通话模块从网络包里解出实时视频数据『流』。
2. 使用模式
说了它的用途,我们看下TextureView工作的模式。刚了解到新浪博客是不支持贴代码的,这是要逼着人说人话。好在此处代码不长,看图说话!
图2 基于TextureView的Camera应用
使用TextureView的步骤:
(1)MainActvity要实现TextureView.SurfaceTextureListener接口
(2)创建TextureView,并将MainActvity设置为SurfaceTextureListener,供系统回调MainActvity实现的onSurfaceTextureXXX接口方法
(3)在onSurfaceTextureAvailable回调中拿到SurfaceTexture,并把它设置给camera,作为承载、预览数据『流』的载体
(4)在onSurfaceTextureDestroyed中关闭预览,释放camera资源,返回true
每个步骤的界面都很清晰。如果大家用过GLSurfaceView,用户代码里要显式的控制SurfaceTexture的声明、生成、下传,而TextureView则把SurfaceTexture干净的从用户视野中拿掉了,这就是被叫做『Texture』View的原因吧!
到这儿,TextureView的用途和使用模式都讲清楚了。看过了咱们的牛,接下来要认真的屠牛啦,Go!
3. Framework代码解析
3.1 TextureView与『上一级』关系
从图1可以看到TextureView与『上一级』(即用户代码中的MainActivity)只存在SurfaceTextureListener这一个关联点,表现为TextureView会在『适当』的时候调用『上一级』实现的SurfaceTextureListener接口方法。
3.2 TextureView内部结构
图1里TextureView与SurfaceTexture构成了组合关系,可见SurfaceTexture的确是由TextureView给『包办』了。在程序世界中,一个对象被『包办』无非是指:
(1)这个对象什么时候被创建?
(2)这个对象如何被创建?
(3)这个对象的产生带来了哪些变化,或者说程序自从有了它有哪些不同?
(4)这个对象什么时候被销毁?
之所以对SurfaceTexture这个对象要大动笔墨,因为它是整个显示框架的『连接者』。
3.3 SurfaceTexture与『上一级』关系
Java层的SurfaceTexture,有setOnFrameAvailableListener方法,其将『上一级』对象(即TextureView)设置为onFrameAvailableListener,这样SurfaceTexture在拿到新的『流』数据时会通知TextureView。
3.4 SurfaceTexture底层结构
SurfaceTexture.java会直接调用SurfaceTexture.cpp中的native方法,注意后者是一层JNI,而非c++类。在创建Java层SurfaceTexture对象时,向下调用SurfaceTexture_init方法,其调用BufferQueue::createBufferQueue静态方法,后者会做以下几件事:
(1)创建BufferQueueCore
(2)将BufferQueueCore作为参数创建BufferQueueProducer
(3)将BufferQueueCore作为参数创建BufferQueueConsumer
BufferQueueCore最重要的对象就是mAllocator,其为IGraphicBufferAlloc类型,其作用只有一个:分配GraphicBuffer内存。其类型是I开头的,我们似乎嗅到了什么。难道一个内存分配器都要通过IPC拿到?对,就是这样!
获取内存分配器的过程为:源进程(比如Camera APK)获取SurfaceComposer服务,通过该服务获取IGraphicBufferAlloc对象,后续内存分配要通过BufferQueueCore对象,都要通过BufferQueueCore的mAllocator对象,最终都要透过SurfaceComposer进程向Gralloc模块分配GraphicBuffer。
我们已经知道BufferQueueCore可以提供强大的GraphicBuffer分配支持。接下来聊聊BufferQueueProducer,从图1看出其为BnGraphicBufferProducer的子类,也是『I』字辈的,对也是用来IPC的家伙!这里分析下图2里onSurfaceTextureAvalilable中调用mCamera.setPreviewTexture(surface)这一瞬间发生的惊人内幕:
(1)快进,直接调到了native层BpCamera->setPreviewTarget(BufferQueueProducer),注意BufferQueueProducer是本地对象,『Bn』辈的小生。
(2)跨进程发生:BufferQueueProducer对象到了内核,内核发现这货是个Binder,拦下,强行登记,只放走了一个『号码牌』(即handle)给目标进程(CameraService进程)。目标进程拿着号码牌,生成了一个BpBinder对象,此时这个BpBinder对象已经拥有了向内核找到源进程、以及源进程中BufferQueueProducer对象的能力。这个BpBinder没闲着,前脚被生成,后脚就被『踹进』了BpGraphicBufferProducer,就是『Bp』辈的后生。
(3)我要内存:CameraServcie可是内存消耗大户,1080p@30fps的YUV数据流,闹着玩呢!要分配内存,怎么干?很简单CameraServcie进程中直接用BpGraphicBufferProducer->dequeueBurffer或者requestBuffer即可。
(4)实际内存分配:BpGraphicBufferProducer因为有BpBinder,自然可以唤醒源进程(Camera APK进程)中的BnGraphicBufferProducer,调用对应的requestBuffer。实作的BnGraphicBufferProducer后生BufferQueueProducer拥有BufferQueueCore,自然可以向SurfaceCompser进程申请到GraphicBuffer,一路返还即可。
剩下的就是CameraServcie进程填充好了GraphicBuffer,然后BpGraphicBufferProducer->enqueue将其置入队列,Camera APK进程中的BufferQueueProducer自然直到这个事件,可以在SurfaceTexuture.cpp这层JNI向上通知frameAvailable。
3.5 聊聊Surface
最后,聊聊Surface这个native层类。Surface继承ANativeWindow,它的核心的就是传入它的IGraphicBufferProducer对象,后续所有对Surface的操作都会传递到IGraphicBufferProducer。在CameraService进程,在拿到BpGraphicBufferProducer后,会在该进程生成一个Surface,传入的正是前者。而Surface则作为ANativeWindow类型,畅通无阻的传入了HAL模块。貌似它的作用就是给Android平台提供统一的一个window接口层。