目录
Surface
官方对Surface的解释是:由屏幕合成器管理的原始缓冲区上的句柄,所谓原生缓冲器,是用于保存当前窗口的像素数据的,也就是说,通过Surface可以获取原生缓冲器以及其中的内容。Surface对应一块屏幕缓冲区,每个Window对应一个Surface,任何View都画在Surface上,Surface中的Canvas,是用于提供画图的地方。
SurfaceView
SurfaceView与普通的View不同,它拥有自己的Surface,它的工作方式是创建一个区别于应用窗口的新窗口,与宿主窗口分离,可以在单独线程中处理业务,不受View的属性控制,无法进行平移缩放等转换,它是通过“双缓冲”机制来达到高效的界面刷新效果。
双缓冲技术是把要处理的图片在内存中处理好之后,再将其显示在屏幕上。双缓冲主要是为了解决反复局部刷屏带来的闪烁。把要画的东西先画到一个内存区域里,然后整体的一次性画出来。
下面通过一个相机预览的功能,来看一下SurfaceView的具体使用
private lateinit var surfaceHolder: SurfaceHolder
获取摄像机管理类,打开某个具体的摄像机,在打开成功的回调里面,创建预览请求
val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager val surfaceView = findViewById<SurfaceView>(R.id.surfaceView) surfaceHolder = surfaceView.holder surfaceHolder.setKeepScreenOn(true) surfaceHolder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(p0: SurfaceHolder) { if (ActivityCompat.checkSelfPermission( this@SurfaceActivity, Manifest.permission.CAMERA ) != PackageManager.PERMISSION_GRANTED ) { //无相机权限 return } try { //获取可用相机设备列表 val cameraIdList = cameraManager.cameraIdList //打开相机 cameraManager.openCamera( cameraIdList[0], //摄像头创建监听 object : CameraDevice.StateCallback() { override fun onOpened(p0: CameraDevice) { //打开摄像头 try { startPreview(p0) } catch (e: CameraAccessException) { e.printStackTrace() } } override fun onDisconnected(p0: CameraDevice) { //关闭摄像头 p0.close() } override fun onError(p0: CameraDevice, p1: Int) { Log.i(tag, "CameraDevice.StateCallback onError") } }, null ) } catch (e: CameraAccessException) { e.printStackTrace() } } override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) { Log.i(tag, "surfaceChanged") } override fun surfaceDestroyed(p0: SurfaceHolder) { Log.i(tag, "surfaceDestroyed") } })
开始预览
private fun startPreview(cameraDevice: CameraDevice) { val captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(surfaceHolder.surface) cameraDevice.createCaptureSession( listOf(surfaceHolder.surface), //会话状态回调 object : CameraCaptureSession.StateCallback() { override fun onConfigured(p0: CameraCaptureSession) { try { // 创建预览需要的CaptureRequest.Builder val previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) previewRequestBuilder.addTarget(surfaceHolder.surface) val previewRequest = previewRequestBuilder.build() p0.setRepeatingRequest(previewRequest, null, null) } catch (e: CameraAccessException) { e.printStackTrace() } } override fun onConfigureFailed(p0: CameraCaptureSession) { Log.i(tag, "onConfigureFailed") } }, null ) }
这样,简易的相机预览功能就出来啦,当然,我们还需要申请CAMERA权限,这里略去。
TextureView
TextureView可以说是一个结合了View和SurfaceTexture的View对象,一个可以把内容流作为外部纹理输出在上面的 View,它只能用于开启了硬件加速的窗口,TextureView不会创建一个独立的窗口,而是像普通的View一样,可以进行平移、旋转等动画,TextureView在Andriod 4.0之后的 API 中才能使用,不过现在的安卓设备基本不存在Andriod 4.0之前的版本了,所以这点不必在意。
TextureView的使用也比较简单,需要获取到它的SurfaceTexture,然后就可以以此来渲染了,下面通过一个简易的视频播放的示例,来瞧瞧它是怎么使用的。
val textureView = findViewById<TextureView>(R.id.textureView) val mediaPlayer = MediaPlayer() textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(p0: SurfaceTexture, p1: Int, p2: Int) { with(mediaPlayer) { setDataSource(GlobalData.videoUrl) setSurface(Surface(p0)) prepare() start() Log.i(tag, "setOnPreparedListener") setOnPreparedListener { it.start() } } } override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture, p1: Int, p2: Int) { } override fun onSurfaceTextureDestroyed(p0: SurfaceTexture): Boolean { mediaPlayer.stop() mediaPlayer.release() return true } override fun onSurfaceTextureUpdated(p0: SurfaceTexture) { } }
SurfaceTexture
SurfaceTexture是Surface和OpenGL ES纹理的组合,用于提供输出到OpenGL ES纹理的Surface,和SurfaceView不同的是,它对图像流的处理并不直接显示,而是转为GL外部纹理,因此可用于图像流数据的二次处理,如Camera滤镜,桌面特效等,但是这样会有若干帧的延迟。同时,由于它本身管理BufferQueue,因此内存消耗也会稍微大一些。比如Camera的预览数据,变成纹理后就可以通过SurfaceTexture交给TextureView作为View层级中的一个硬件加速层来显示。
SurfaceView和TextureView的区别
- SurfaceView是直接输出的,拥有自己独立Surface,它的渲染可以放在单独线程,其缺点是不能做变形和动画。
- TextureView是一个可以把内容流作为外部纹理输出的,本身必须是一个硬件加速层,显示画面更新时有1~3帧的延迟。
- TextureView本身也包含了SurfaceTexture,它与SurfaceView+SurfaceTexture组合相比,也可以把内容流上的图像转成纹理输出,区别在于TextureView是在View层级中绘制的,而SurfaceView+SurfaceTexture在单独的Surface上做绘制的。
SurfaceView | TextureView |
---|---|
占用内存低 | 占用内存高 |
耗电低 | 耗电高 |
绘制及时 | 1-3帧延时 |
不支持动画截图 | 支持动画截图 |
个人觉得,对于需要不断更新画布的游戏来说,SurfaceView是最好的选择,对于视频播放器或相机应用的开发,则TextureView更加适合。