Android-TextureView的原理分析及使用


图1 TextureView及其依赖的java/c++类

本文将从TetureView的用途、使用模式及其在Framework依赖的类(图1所示那些)的三个方面进行说明。

1. 用途

TextureView可用于承载显示『数据流』的场合,之前看到『流』不太明确其意义,这里给两个具体的场景大家体会一下:camera模块从sensor采集了[email protected]的预览数据『流』,视频通话模块从网络包里解出实时视频数据『流』。

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可是内存消耗大户,[email protected]的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接口层。

3.6 TextureView如何使用?

如果你想显示一段在线视频或者任意的数据流比如视频或者OpenGL 场景,你可以用android中的TextureView做到。

TextureView的兄弟SurfaceView

应用程序的视频或者opengl内容往往是显示在一个特别的UI控件中:SurfaceView。SurfaceView的工作方式是创建一个置于应用窗口之后的新窗口。这种方式的效率非常高,因为SurfaceView窗口刷新的时候不需要重绘应用程序的窗口(android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率非常低下,不过满足普通应用界面的需求还是绰绰有余),但是SurfaceView也有一些非常不便的限制。

因为SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()

为了解决这个问题 Android 4.0中引入了TextureView。

TextureView

与SurfaceView相比,TextureView并没有创建一个单独的Surface用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等。另外,Textureview必须在硬件加速开启的窗口中。

TextureView的使用非常简单,你唯一要做的就是获取用于渲染内容的SurfaceTexture。具体做法是先创建TextureView对象,然后实现SurfaceTextureListener接口,代码如下:

 
  
  1. private TextureView myTexture;
  2. public class MainActivity extends Activity implements SurfaceTextureListener{
  3. protected void onCreate(Bundle savedInstanceState) {
  4. myTexture = new TextureView(this);
  5. myTexture.setSurfaceTextureListener(this);
  6. setContentView(myTexture);
  7. }
  8. }

Activity implementsSurfaceTextureListener接口因此activity中需要重写如下方法:

 
  
  1. @Override
  2. public void onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1, int arg2) {
  3. }
  4. @Override
  5. public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
  6. }
  7. @Override
  8. public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,int arg2) {
  9. }
  10. @Override
  11. public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
  12. }


TextureView可以使用setAlphasetRotation方法达到改变透明度和旋转的效果。

 
  
  1. myTexture.setAlpha(1.0f);
  2. myTexture.setRotation(90.0f);

除了上面的方法之外,TextureView 还有如下方法:

序号 方法&描述
1 getSurfaceTexture()
This method returns the SurfaceTexture used by this view.
2 getBitmap(int width, int height)
This method returns Returns a Bitmap representation of the content of the associated surface texture.
3 getTransform(Matrix transform)
This method returns the transform associated with this texture view.
4 isOpaque()
This method indicates whether this View is opaque.
5 lockCanvas()
This method start editing the pixels in the surface
6 setOpaque(boolean opaque)
This method indicates whether the content of this TextureView is opaque.
7 setTransform(Matrix transform)
This method sets the transform to associate with this texture view.
8 unlockCanvasAndPost(Canvas canvas)
This method finish editing pixels in the surface.

例子

下面的例子演示了如何使用TextureView类,我们创建了一个可以在TextureView中预览Camera的demo,可以改变它的角度以及方向。当然程序需要运行在有摄像头的设备上。

下面是MainActivity.java中的代码:

 
  
  1. package com.example.textureview;
  2. import java.io.IOException;
  3. import android.annotation.SuppressLint;
  4. import android.app.Activity;
  5. import android.graphics.SurfaceTexture;
  6. import android.hardware.Camera;
  7. import android.os.Bundle;
  8. import android.view.Gravity;
  9. import android.view.Menu;
  10. import android.view.TextureView;
  11. import android.view.TextureView.SurfaceTextureListener;
  12. import android.view.View;
  13. import android.widget.FrameLayout;
  14. public class MainActivity extends Activity implements SurfaceTextureListener {
  15. private TextureView myTexture;
  16. private Camera mCamera;
  17. @SuppressLint("NewApi")
  18. @Override
  19. protected void onCreate(Bundle savedInstanceState) {
  20. super.onCreate(savedInstanceState);
  21. setContentView(R.layout.activity_main);
  22. myTexture = new TextureView(this);
  23. myTexture.setSurfaceTextureListener(this);
  24. setContentView(myTexture);
  25. }
  26. @Override
  27. public boolean onCreateOptionsMenu(Menu menu) {
  28. // Inflate the menu; this adds items to the action bar if it is present.
  29. getMenuInflater().inflate(R.menu.main, menu);
  30. return true;
  31. }
  32. @SuppressLint("NewApi")
  33. @Override
  34. public void onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1,
  35. int arg2) {
  36. mCamera = Camera.open();
  37. Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
  38. myTexture.setLayoutParams(new FrameLayout.LayoutParams(
  39. previewSize.width, previewSize.height, Gravity.CENTER));
  40. try {
  41. mCamera.setPreviewTexture(arg0);
  42. } catch (IOException t) {
  43. }
  44. mCamera.startPreview();
  45. myTexture.setAlpha(1.0f);
  46. myTexture.setRotation(90.0f);
  47. }
  48. @Override
  49. public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
  50. mCamera.stopPreview();
  51. mCamera.release();
  52. return true;
  53. }
  54. @Override
  55. public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,
  56. int arg2) {
  57. // TODO Auto-generated method stub
  58. }
  59. @Override
  60. public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
  61. // TODO Auto-generated method stub
  62. }
  63. }

activity_main.xml

 
  
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:paddingBottom="@dimen/activity_vertical_margin"
  6. android:paddingLeft="@dimen/activity_horizontal_margin"
  7. android:paddingRight="@dimen/activity_horizontal_margin"
  8. android:paddingTop="@dimen/activity_vertical_margin"
  9. tools:context=".MainActivity" >
  10. <TextureView
  11. android:id="@+id/textureView1"
  12. android:layout_width="wrap_content"
  13. android:layout_height="wrap_content"
  14. android:layout_alignParentTop="true"
  15. android:layout_centerHorizontal="true" />
  16. </RelativeLayout>

AndroidManifest.xml

 
  
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.textureview"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6. <uses-sdk
  7. android:minSdkVersion="8"
  8. android:targetSdkVersion="17" />
  9. <uses-permission android:name="android.permission.CAMERA"/>
  10. <application
  11. android:allowBackup="true"
  12. android:icon="@drawable/ic_launcher"
  13. android:label="@string/app_name"
  14. android:theme="@style/AppTheme" >
  15. <activity
  16. android:name="com.example.textureview.MainActivity"
  17. android:label="@string/app_name" >
  18. <intent-filter>
  19. <action android:name="android.intent.action.MAIN" />
  20. <category android:name="android.intent.category.LAUNCHER" />
  21. </intent-filter>
  22. </activity>
  23. </application>
  24. </manifest>


不同参数下的截图:

myTexture.setAlpha(0.5f);

myTexture.setRotation(45.0f);

Anroid TextureView Tutorial

myTexture.setAlpha(1.5f);

myTexture.setRotation(45.0f);

Anroid TextureView Tutorial

myTexture.setAlpha(1.0f);

myTexture.setRotation(90.0f);

Anroid TextureView Tutorial

可参考:
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1213/2153.html
http://blog.sina.com.cn/s/blog_4b968a420102wjqx.html

8