3、Camera2 预览实战:配置 TextureView、开启相机、请求预览流、处理生命周期
在上一节中,我们了解了 Camera2 的核心架构与关键类。本节将进入实战环节,手把手带你实现一个完整的 Camera2 预览功能。我们将从 UI 组件 TextureView 的配置开始,逐步完成相机开启、预览流请求以及生命周期管理的全流程。
3.1 配置 TextureView
TextureView 是 Android 中用于显示相机预览画面的核心控件。与 SurfaceView 不同,它支持硬件加速和动画变换,更适合与 Camera2 的 SurfaceTexture 配合使用。
步骤 1:在布局文件中添加 TextureView
<!-- activity_camera.xml -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
步骤 2:在 Activity 中初始化并设置监听器
public class CameraActivity extends AppCompatActivity {
private TextureView textureView;
private CameraManager cameraManager;
private String cameraId;
private CameraDevice cameraDevice;
private CameraCaptureSession captureSession;
private CaptureRequest.Builder previewRequestBuilder;
private CaptureRequest previewRequest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
textureView = findViewById(R.id.texture_view);
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
// 关键:TextureView 必须在 SurfaceTexture 就绪后才能操作相机
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
// SurfaceTexture 已就绪,可以安全地打开相机
openCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// 处理尺寸变化(例如屏幕旋转)
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
// 释放相机资源
closeCamera();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// 每帧更新时回调(通常不需要处理)
}
});
}
}
3.2 开启相机
开启相机需要先获取 CameraManager,然后通过 openCamera 方法异步打开。我们需要指定相机 ID(通常后置为 "0",前置为 "1")以及一个状态回调。
private void openCamera() {
try {
// 获取后置摄像头 ID
cameraId = cameraManager.getCameraIdList()[0];
// 配置相机特性(可选:设置输出尺寸等)
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
// 这里可以获取支持的预览尺寸,选择最合适的
// 打开相机,传入状态回调
cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
cameraDevice = camera;
// 相机已打开,开始创建预览会话
createPreviewSession();
}
@Override
public void onDisconnected(CameraDevice camera) {
cameraDevice.close();
cameraDevice = null;
}
@Override
public void onError(CameraDevice camera, int error) {
cameraDevice.close();
cameraDevice = null;
// 处理错误(例如相机被占用)
}
}, null); // 第三个参数是 Handler,null 表示使用主线程
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
3.3 请求预览流
预览流的核心是创建 CameraCaptureSession,并将 TextureView 的 SurfaceTexture 作为输出目标。
private void createPreviewSession() {
try {
// 获取 TextureView 的 SurfaceTexture
SurfaceTexture surfaceTexture = textureView.getSurfaceTexture();
// 设置默认缓冲区大小(通常设置为预览尺寸)
surfaceTexture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
// 创建 Surface 对象,用于 Camera2 输出
Surface surface = new Surface(surfaceTexture);
// 创建预览请求(TEMPLATE_PREVIEW 是标准预览模板)
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
previewRequestBuilder.addTarget(surface);
// 创建捕获会话
cameraDevice.createCaptureSession(Arrays.asList(surface),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
captureSession = session;
// 构建最终的预览请求
previewRequest = previewRequestBuilder.build();
// 开始重复请求(持续预览)
try {
captureSession.setRepeatingRequest(previewRequest,
null, // 预览回调(可选)
null // Handler
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
// 配置失败处理
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
3.4 处理生命周期
Camera2 资源必须与 Activity/Fragment 的生命周期严格绑定,否则会导致内存泄漏或相机被占用。
核心原则:
- onResume():如果 TextureView 已就绪,则打开相机。
- onPause():释放相机资源(关闭会话和相机设备)。
- onDestroy():确保所有资源被释放。
@Override
protected void onResume() {
super.onResume();
// 如果 TextureView 的 SurfaceTexture 已经就绪,直接打开相机
if (textureView.isAvailable()) {
openCamera();
} else {
// 否则等待 SurfaceTextureListener 回调
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
protected void onPause() {
closeCamera();
super.onPause();
}
private void closeCamera() {
if (captureSession != null) {
captureSession.close();
captureSession = null;
}
if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
3.5 完整代码整合与注意事项
将上述代码整合到 CameraActivity 中,即可实现一个基本的 Camera2 预览。以下是几个关键注意事项:
| 注意事项 | 说明 |
|---|---|
| 权限声明 | 在 AndroidManifest.xml 中添加 <uses-permission android:name="android.permission.CAMERA" />,并在运行时请求权限(Android 6.0+)。 |
| 预览尺寸选择 | 通过 StreamConfigurationMap.getOutputSizes(SurfaceTexture.class) 获取支持的尺寸,选择与屏幕比例最接近的尺寸,避免变形。 |
| 线程模型 | Camera2 的回调默认在主线程执行。如果需要在子线程处理,可以传入 Handler 参数。 |
| 屏幕旋转 | 在 onSurfaceTextureSizeChanged 中调用 configureTransform 方法,通过矩阵旋转校正预览方向。 |
| 错误处理 | 务必处理 CameraAccessException(相机被占用)和 SecurityException(权限不足)。 |
通过以上步骤,你已经成功搭建了一个完整的 Camera2 预览框架。下一节我们将深入讲解如何调整预览方向、选择最佳预览尺寸,以及如何实现前后摄像头的切换。