You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Camera2录屏应用报错:TextureView.getSurfaceTexture()返回Null

解决Camera2应用中TextureView.getSurfaceTexture()返回null的空指针问题

问题根源

报错的核心原因是TextureView的SurfaceTexture尚未完成初始化就调用了getSurfaceTexture()。TextureView需要先完成布局绘制、附加到窗口后才会创建SurfaceTexture,原代码直接在相机打开的onOpened回调里启动预览,此时TextureView可能还没准备好,导致返回null触发空指针异常。

同时原代码还存在后台线程未初始化的问题,backgroundHandler为null会导致相机预览请求无法正确提交。

修复方案

通过监听TextureView的SurfaceTexture生命周期,确保仅在SurfaceTexture可用时才初始化相机;同时补全后台线程的生命周期管理,符合Android应用规范。

修改后的关键代码

  1. 添加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);
    }
}
  1. 补全后台线程与相机资源的生命周期管理
@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;
    }
}
  1. 优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 01:22:02