Camera2录屏应用报错:TextureView.getSurfaceTexture()返回Null
解决Camera2应用中TextureView.getSurfaceTexture()返回null的空指针问题
问题根源
报错的核心原因是TextureView的SurfaceTexture尚未完成初始化就调用了getSurfaceTexture()。TextureView需要先完成布局绘制、附加到窗口后才会创建SurfaceTexture,原代码直接在相机打开的onOpened回调里启动预览,此时TextureView可能还没准备好,导致返回null触发空指针异常。
同时原代码还存在后台线程未初始化的问题,backgroundHandler为null会导致相机预览请求无法正确提交。
修复方案
通过监听TextureView的SurfaceTexture生命周期,确保仅在SurfaceTexture可用时才初始化相机;同时补全后台线程的生命周期管理,符合Android应用规范。
修改后的关键代码
- 添加SurfaceTexture监听器,确保SurfaceTexture就绪后再启动相机
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); previewView = findViewById(R.id.previewView); // 监听TextureView的SurfaceTexture状态 previewView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // SurfaceTexture就绪,启动相机 if (allPermissionsGranted()) { startCamera(); } } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { // 销毁时释放相机资源 closeCamera(); return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) {} }); Button recordButton = findViewById(R.id.recordButton); recordButton.setOnClickListener(view -> { if (!isRecording) { startRecording(); } else { stopRecording(); } }); // 仅请求权限,相机启动交给SurfaceTexture回调 if (!allPermissionsGranted()) { ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CAMERA_PERMISSION); } }
- 补全后台线程与相机资源的生命周期管理
@Override protected void onResume() { super.onResume(); startBackgroundThread(); // 若TextureView已就绪,重新启动相机 if (previewView.isAvailable() && allPermissionsGranted()) { startCamera(); } } @Override protected void onPause() { closeCamera(); stopBackgroundThread(); super.onPause(); } private void startBackgroundThread() { backgroundThread = new HandlerThread("CameraBackground"); backgroundThread.start(); backgroundHandler = new Handler(backgroundThread.getLooper()); } private void stopBackgroundThread() { backgroundThread.quitSafely(); try { backgroundThread.join(); backgroundThread = null; backgroundHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } } private void closeCamera() { closePreviewSession(); if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; } }
- 优化startPreview方法,增加空判断与合理的模板选择
private void startPreview() { try { closePreviewSession(); SurfaceTexture texture = previewView.getSurfaceTexture(); if (texture == null) { return; // SurfaceTexture未就绪,直接返回 } texture.setDefaultBufferSize(videoSize.getWidth(), videoSize.getHeight()); Surface previewSurface = new Surface(texture); // 预览阶段使用TEMPLATE_PREVIEW模板,录制时再切换为TEMPLATE_RECORD captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureRequestBuilder.addTarget(previewSurface); // 使用后台Handler处理相机会话回调,避免主线程阻塞 cameraDevice.createCaptureSession(Collections.singletonList(previewSurface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { if (cameraDevice == null) { return; } cameraCaptureSession = session; updatePreview(); } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { Toast.makeText(MainActivity.this, "预览配置失败", Toast.LENGTH_SHORT).show(); } }, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
额外说明
- 预览阶段使用
TEMPLATE_PREVIEW模板,录制时再切换为TEMPLATE_RECORD,符合Camera2的最佳实践 - 权限请求通过后,若TextureView已就绪会自动启动相机,无需手动触发
- 所有相机操作都在后台线程执行,避免主线程ANR
内容的提问来源于stack exchange,提问作者Matheus Fortunato Alves
相关产品推荐
相关产品推荐

