Camera2原生api级 Demo答
回答1:
预览黑屏的核心问题在于 GLSurfaceView 初始化时机错误、相机后台线程未提前启动 以及 YUV 数据传递链路不完整。以下是修复后的完整代码,关键修改已标注注释:
package com.android.example.cameraappxjava;import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.widget.Button;
import android.widget.Toast;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;import com.android.example.cameraappxjava.util.CameraGLRenderer;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;/*** 调用自定义渲染器的Demo:模拟YUV预览渲染(修复预览黑屏问题)*/
public class MainActivity2 extends AppCompatActivity {private static final String TAG = "camera2api";private static final int REQUEST_CAMERA_PERMISSION = 100;// GLSurfaceView 核心组件private GLSurfaceView glSurfaceView;private CameraGLRenderer cameraGLRenderer;// 相机核心组件private ImageReader previewImageReader; // 预览用YUV ImageReaderprivate ImageReader captureImageReader; // 拍照用JPEG ImageReaderprivate Button captureButton;private CameraDevice cameraDevice;private CameraCaptureSession cameraCaptureSession;private CaptureRequest.Builder captureRequestBuilder;private String cameraId;private Handler backgroundHandler; // 相机后台线程Handler(必须提前初始化)private HandlerThread backgroundThread;private CameraManager manager;private volatile boolean isCapturing = false;private StreamConfigurationMap map;private long lastClickTime = 0;private static final long MIN_CLICK_INTERVAL = 1000;private ContentResolver resolver;private ContentValues values;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "onCreate ——————————————————————");// 【关键修复1】提前启动相机后台线程(确保预览ImageReader回调有线程执行)startBackgroundThread();// 1. 初始化GLSurfaceView(必须在主线程初始化)glSurfaceView = findViewById(R.id.glsurfaceView);// 2. 初始化拍照按钮captureButton = findViewById(R.id.btnCapture);// 3. 配置GLSurfaceView + 自定义渲染器(核心)initGLRenderer();// 4. 初始化相机参数(此时backgroundHandler已存在,避免空指针)initCamera();// 5. 拍照按钮监听captureButton.setOnClickListener(v -> {long currentTime = SystemClock.elapsedRealtime();if (currentTime - lastClickTime > MIN_CLICK_INTERVAL) {lastClickTime = currentTime;takePicture();} else {Log.d(TAG, "点击过快,已忽略");}});}/*** 初始化GLSurfaceView和渲染器(修复:确保渲染器与GLSurfaceView绑定完整)*/private void initGLRenderer() {// 1. 设置OpenGL版本(2.0兼容所有设备)glSurfaceView.setEGLContextClientVersion(2);// 2. 创建渲染器实例(提前创建,避免onSurfaceCreated时为空)cameraGLRenderer = new CameraGLRenderer();// 3. 绑定渲染器(关键:确保GLSurfaceView与渲染器关联)glSurfaceView.setRenderer(cameraGLRenderer);// 4. 按需渲染(有新帧才重绘,节省性能)glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);}/*** 初始化相机参数(修复:预览ImageReader绑定正确的线程)*/private void initCamera() {Log.d(TAG, "initCamera: 初始化相机配置");try {// 1. 初始化CameraManager和相机IDmanager = (CameraManager) getSystemService(CAMERA_SERVICE);cameraId = manager.getCameraIdList()[0]; // 默认使用后置相机CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);if (map == null) {Log.e(TAG, "错误: StreamConfigurationMap为空!!");return;}// 2. 初始化预览用ImageReader(YUV_420_888格式,核心修复:绑定backgroundHandler)Size[] yuvSizes = map.getOutputSizes(ImageFormat.YUV_420_888);// 【关键修复2】选择与GLSurfaceView比例匹配的预览尺寸(避免拉伸/黑屏)Size previewSize = chooseOptimalSize(yuvSizes, glSurfaceView.getWidth(), glSurfaceView.getHeight());previewImageReader = ImageReader.newInstance(previewSize.getWidth(),previewSize.getHeight(),ImageFormat.YUV_420_888,2 // 2个缓冲区,避免帧丢失);// 预览帧回调(核心:将YUV数据传给渲染器,必须用backgroundHandler)previewImageReader.setOnImageAvailableListener(reader -> {try (Image image = reader.acquireLatestImage()) { // try-with-resources自动关闭Imageif (image == null || cameraGLRenderer == null) {Log.w(TAG, "预览帧为空或渲染器未初始化");return;}// 【关键修复3】传递Image前检查格式(确保是YUV_420_888)if (image.getFormat() != ImageFormat.YUV_420_888) {Log.e(TAG, "预览帧格式错误,不是YUV_420_888");return;}// 传递YUV数据给渲染器,并触发重绘cameraGLRenderer.setYUVData(image);glSurfaceView.requestRender(); // 触发渲染器onDrawFrame} catch (Exception e) {Log.e(TAG, "预览帧处理失败: " + e.getMessage(), e);}}, backgroundHandler); // 必须绑定相机后台线程,避免主线程阻塞// 3. 初始化拍照用ImageReader(JPEG格式,保留原有逻辑)Size[] jpegSizes = map.getOutputSizes(ImageFormat.JPEG);Size captureSize = chooseOptimalSize(jpegSizes, getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels);if (captureImageReader == null || captureImageReader.getWidth() != captureSize.getWidth()) {if (captureImageReader != null) captureImageReader.close();captureImageReader = ImageReader.newInstance(captureSize.getWidth(),captureSize.getHeight(),ImageFormat.JPEG,2);}// 4. 图片保存参数初始化resolver = getContentResolver();values = new ContentValues();values.put(MediaStore.Images.Media.DISPLAY_NAME, "pic_" + System.currentTimeMillis() + ".jpg");values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);} catch (CameraAccessException e) {Log.e(TAG, "相机访问异常: " + e.getMessage(), e);} catch (NullPointerException e) {Log.e(TAG, "NPE: " + e.getMessage(), e);}}/*** 打开相机(修复:检查backgroundHandler是否就绪)*/private void openCamera() {// 【关键修复4】检查核心组件是否就绪(避免空指针导致相机打开失败)if (previewImageReader == null || backgroundHandler == null || cameraGLRenderer == null) {Log.w(TAG, "预览组件未就绪,1000ms后重试");backgroundHandler.postDelayed(this::openCamera, 1000);return;}Log.d(TAG, "openCamera: 尝试打开相机");try {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {Log.i(TAG, "1.打开相机: " + cameraId);// 打开相机时绑定backgroundHandler(确保相机回调在后台线程执行)manager.openCamera(cameraId, stateCallback, backgroundHandler);} else {Log.w(TAG, "相机权限未授予");}} catch (CameraAccessException e) {Log.e(TAG, "打开相机失败: " + e.getMessage(), e);} catch (SecurityException e) {Log.e(TAG, "安全异常: " + e.getMessage(), e);}}/*** 创建相机预览会话(修复:确保双Surface都添加到会话)*/private void createCameraPreviewSession() {if (cameraDevice == null || previewImageReader == null || captureImageReader == null) {Log.e(TAG, "创建预览会话失败: 相机或ImageReader不可用");return;}try {// 1. 获取预览和拍照的Surface(核心:两个Surface都要添加到会话)Surface previewSurface = previewImageReader.getSurface();Surface captureSurface = captureImageReader.getSurface();// 2. 配置输出Surface列表(预览+拍照)List<Surface> outputSurfaces = new ArrayList<>(2);outputSurfaces.add(previewSurface); // 预览Surface(必须第一个添加)outputSurfaces.add(captureSurface); // 拍照Surface// 3. 创建相机会话cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {Log.i(TAG, "2.2 预览会话配置成功");cameraCaptureSession = session;try {// 配置预览请求(目标是预览Surface)captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);captureRequestBuilder.addTarget(previewSurface); // 绑定预览Surface// 配置自动对焦和闪光灯(确保预览清晰)captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);Log.i(TAG, "3.开始下发预览请求");// 下发重复预览请求(核心:预览帧持续输出)cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "设置预览请求失败: " + e.getMessage(), e);}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {Log.e(TAG, "预览会话配置失败");Toast.makeText(MainActivity2.this, "相机配置失败,请重试", Toast.LENGTH_SHORT).show();}}, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "创建预览会话异常: " + e.getMessage(), e);}}/*** 拍照逻辑(保留原有功能,修复:拍照后恢复预览)*/private void takePicture() {Log.i(TAG, "4.开始拍照流程——————————");try {if (!checkTakePicture()) {Log.i(TAG, "拍照流程检查未通过!");return;}// 配置拍照请求(目标是拍照Surface)CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureBuilder.addTarget(captureImageReader.getSurface());// 配置拍照参数(自动模式+旋转角度)captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);int rotation = getWindowManager().getDefaultDisplay().getRotation();captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, rotation);// 拍照回调(保存图片)captureImageReader.setOnImageAvailableListener(reader -> {Log.d(TAG, "拍照图像数据可用");try (Image image = reader.acquireLatestImage()) {if (image != null) {// 提取JPEG数据Image.Plane[] planes = image.getPlanes();ByteBuffer buffer = planes[0].getBuffer();byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);// 保存图片到相册saveImage(bytes);// 提示用户runOnUiThread(() ->Toast.makeText(MainActivity2.this, "图片已保存到相册", Toast.LENGTH_SHORT).show());}} catch (Exception e) {Log.e(TAG, "保存拍照图像错误: " + e.getMessage(), e);} finally {isCapturing = false;// 【关键修复5】拍照后恢复预览(重新下发预览请求)if (cameraCaptureSession != null && captureRequestBuilder != null) {try {cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "恢复预览失败: " + e.getMessage(), e);}}}}, backgroundHandler);// 停止预览,执行拍照Log.d(TAG, "停止预览,准备拍照");cameraCaptureSession.stopRepeating();isCapturing = true;cameraCaptureSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {super.onCaptureFailed(session, request, failure);Log.e(TAG, "拍照失败: " + failure.getReason());isCapturing = false;// 拍照失败也恢复预览if (cameraCaptureSession != null && captureRequestBuilder != null) {try {cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "恢复预览失败: " + e.getMessage(), e);}}}}, backgroundHandler);} catch (CameraAccessException | IllegalStateException | SecurityException e) {Log.e(TAG, "拍照过程异常: " + e.getClass().getSimpleName(), e);isCapturing = false;}}/*** 保存图片到相册(修复:使用MediaStore避免文件权限问题)*/private void saveImage(byte[] bytes) {if (bytes == null || bytes.length == 0) {Log.e(TAG, "保存失败:图片数据为空");return;}// 更新文件名(避免重复)values.put(MediaStore.Images.Media.DISPLAY_NAME, "pic_" + System.currentTimeMillis() + ".jpg");Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);if (imageUri == null) {Log.e(TAG, "保存失败:无法获取图片Uri");return;}try (FileOutputStream output = (FileOutputStream) resolver.openOutputStream(imageUri)) {output.write(bytes);Log.i(TAG, "图像保存成功, 大小: " + bytes.length + " bytes, Uri: " + imageUri);} catch (IOException e) {Log.e(TAG, "保存文件失败: " + e.getMessage(), e);// 保存失败删除无效Uriresolver.delete(imageUri, null, null);}}/*** 检查拍照前的组件状态*/private boolean checkTakePicture() {if (cameraDevice == null) {Log.w(TAG, "拍照失败: 相机未初始化");return false;}if (cameraCaptureSession == null) {Log.e(TAG, "拍照错误: CameraCaptureSession为空");return false;}if (backgroundHandler == null) {Log.e(TAG, "拍照错误: backgroundHandler未
回答2:
根据您提供的代码,预览黑屏的核心问题在于 相机后台线程初始化时机错误 和 GLSurfaceView 渲染线程与相机线程的数据同步问题。以下是修复后的完整代码及关键修改说明:
一、关键问题分析
backgroundHandler 初始化延迟:initCamera() 中初始化预览 ImageReader 时,backgroundHandler 尚未创建(startBackgroundThread() 在 onResume() 中调用),导致 ImageReader 回调无法在相机后台线程执行,预览帧无法接收。
GLSurfaceView 纹理坐标颠倒:当前纹理坐标未适配相机默认横屏输出,导致预览画面颠倒(视觉上表现为黑屏或异常)。
权限授予后未恢复预览:onRequestPermissionsResult() 中授予权限后未调用 glSurfaceView.onResume(),导致 OpenGL 上下文未初始化。
MainActivity2.java
package com.android.example.cameraappxjava;import android.Manifest;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.net.Uri;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.widget.Button;
import android.widget.Toast;import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;import com.android.example.cameraappxjava.util.CameraGLRenderer;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;public class MainActivity2 extends AppCompatActivity {private static final String TAG = "camera2api";private static final int REQUEST_CAMERA_PERMISSION = 100;// GLSurfaceView 相关private GLSurfaceView glSurfaceView;private CameraGLRenderer cameraGLRenderer;// 相机相关private ImageReader previewImageReader; // 预览用(YUV_420_888)private ImageReader captureImageReader; // 拍照用(JPEG)private Button captureButton;private CameraDevice cameraDevice;private CameraCaptureSession cameraCaptureSession;private CaptureRequest.Builder captureRequestBuilder;private String cameraId;private Handler backgroundHandler;private HandlerThread backgroundThread;private CameraManager manager;private volatile boolean isCapturing = false;private StreamConfigurationMap map;private long lastClickTime = 0;private static final long MIN_CLICK_INTERVAL = 1000;private ContentResolver resolver;private ContentValues values;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d(TAG, "onCreate ——————————————————————");// 初始化 GLSurfaceViewglSurfaceView = findViewById(R.id.glsurfaceView);// 初始化拍照按钮captureButton = findViewById(R.id.btnCapture);// 初始化 GLSurfaceView 和渲染器initGLRenderer();// 初始化相机参数(仅配置基础信息,ImageReader 在后台线程启动后初始化)initCameraBaseConfig();// 拍照按钮监听captureButton.setOnClickListener(v -> {long currentTime = SystemClock.elapsedRealtime();if (currentTime - lastClickTime > MIN_CLICK_INTERVAL) {lastClickTime = currentTime;takePicture();} else {Log.d(TAG, "点击过快,已忽略");}});}/*** 初始化 GLSurfaceView 和自定义渲染器*/private void initGLRenderer() {glSurfaceView.setEGLContextClientVersion(2);cameraGLRenderer = new CameraGLRenderer();glSurfaceView.setRenderer(cameraGLRenderer);glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); // 按需渲染}/*** 初始化相机基础配置(不包含 ImageReader,避免 backgroundHandler 为空)*/private void initCameraBaseConfig() {Log.d(TAG, "initCameraBaseConfig: 初始化相机基础配置");try {manager = (CameraManager) getSystemService(CAMERA_SERVICE);cameraId = manager.getCameraIdList()[0]; // 默认使用后置相机CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);if (map == null) {Log.e(TAG, "错误: StreamConfigurationMap为空!!");return;}// 初始化图片保存参数resolver = getContentResolver();values = new ContentValues();values.put(MediaStore.Images.Media.DISPLAY_NAME, "pic_" + System.currentTimeMillis() + ".jpg");values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);} catch (CameraAccessException e) {Log.e(TAG, "相机访问异常: " + e.getMessage());}}/*** 在后台线程启动后初始化 ImageReader(确保 backgroundHandler 不为空)*/private void initImageReaders() {if (map == null || backgroundHandler == null || glSurfaceView == null) {Log.e(TAG, "initImageReaders: 初始化条件不满足");return;}// 1. 初始化预览用 ImageReader(YUV_420_888)Size[] yuvSizes = map.getOutputSizes(ImageFormat.YUV_420_888);Size previewSize = chooseOptimalSize(yuvSizes, glSurfaceView.getWidth(), glSurfaceView.getHeight());if (previewImageReader != null) previewImageReader.close();previewImageReader = ImageReader.newInstance(previewSize.getWidth(),previewSize.getHeight(),ImageFormat.YUV_420_888,2 // 2个缓冲区,避免帧丢失);// 设置预览帧回调(在相机后台线程执行)previewImageReader.setOnImageAvailableListener(reader -> {try (Image image = reader.acquireLatestImage()) {if (image == null || cameraGLRenderer == null) return;// 传递 YUV 数据给渲染器并触发重绘cameraGLRenderer.setYUVData(image);glSurfaceView.requestRender();} catch (Exception e) {Log.e(TAG, "预览帧处理失败: " + e.getMessage());}}, backgroundHandler);// 2. 初始化拍照用 ImageReader(JPEG)Size[] jpegSizes = map.getOutputSizes(ImageFormat.JPEG);Size captureSize = chooseOptimalSize(jpegSizes, glSurfaceView.getWidth(), glSurfaceView.getHeight());if (captureImageReader != null) captureImageReader.close();captureImageReader = ImageReader.newInstance(captureSize.getWidth(),captureSize.getHeight(),ImageFormat.JPEG,2);}/*** 打开相机(确保 ImageReader 和后台线程已就绪)*/private void openCamera() {if (previewImageReader == null || backgroundHandler == null || cameraId == null) {Log.w(TAG, "预览ImageReader或后台线程未就绪,1000ms后重试");backgroundHandler.postDelayed(this::openCamera, 1000);return;}Log.d(TAG, "openCamera: 尝试打开相机");try {if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {Log.i(TAG, "打开相机: " + cameraId);manager.openCamera(cameraId, stateCallback, backgroundHandler);} else {Log.w(TAG, "相机权限未授予");}} catch (CameraAccessException e) {Log.e(TAG, "打开相机失败: " + e.getMessage());} catch (SecurityException e) {Log.e(TAG, "安全异常: " + e.getMessage());}}/*** 创建相机预览会话*/private void createCameraPreviewSession() {if (cameraDevice == null || previewImageReader == null || captureImageReader == null) {Log.e(TAG, "创建预览会话失败: 相机或ImageReader不可用");return;}try {// 获取预览和拍照的 SurfaceSurface previewSurface = previewImageReader.getSurface();Surface captureSurface = captureImageReader.getSurface();// 配置输出 Surface 列表(预览 + 拍照)List<Surface> outputSurfaces = new ArrayList<>(2);outputSurfaces.add(previewSurface);outputSurfaces.add(captureSurface);// 创建捕获会话cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {@Overridepublic void onConfigured(@NonNull CameraCaptureSession session) {Log.i(TAG, "预览会话配置成功");cameraCaptureSession = session;try {// 配置预览请求captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);captureRequestBuilder.addTarget(previewSurface); // 预览目标为 ImageReader 的 Surface// 自动对焦和曝光captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);// 下发预览请求cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "设置预览请求失败: " + e.getMessage());}}@Overridepublic void onConfigureFailed(@NonNull CameraCaptureSession session) {Log.e(TAG, "预览会话配置失败");Toast.makeText(MainActivity2.this, "相机配置失败", Toast.LENGTH_SHORT).show();}}, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "创建预览会话异常: " + e.getMessage());}}/*** 拍照逻辑*/private void takePicture() {Log.i(TAG, "开始拍照流程");if (!checkTakePicture()) {Log.i(TAG, "拍照检查未通过");return;}try {// 配置拍照请求CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);captureBuilder.addTarget(captureImageReader.getSurface());// 自动模式和旋转角度captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);int rotation = getWindowManager().getDefaultDisplay().getRotation();captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, rotation);// 拍照回调(保存图片)captureImageReader.setOnImageAvailableListener(reader -> {Log.d(TAG, "拍照图像数据可用");try (Image image = reader.acquireLatestImage()) {if (image != null) {// 提取 JPEG 数据Image.Plane[] planes = image.getPlanes();ByteBuffer buffer = planes[0].getBuffer();byte[] bytes = new byte[buffer.remaining()];buffer.get(bytes);// 保存图片saveImage(bytes);}} catch (Exception e) {Log.e(TAG, "保存拍照图像错误: " + e.getMessage());} finally {isCapturing = false;// 恢复预览if (cameraCaptureSession != null && captureRequestBuilder != null) {try {cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);} catch (CameraAccessException e) {Log.e(TAG, "恢复预览失败: " + e.getMessage());}}}}, backgroundHandler);// 停止预览并执行拍照cameraCaptureSession.stopRepeating();isCapturing = true;cameraCaptureSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() {@Overridepublic void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {super.onCaptureFailed(session, request, failure);Log.e(TAG, "拍照失败: " + failure.getReason());isCapturing = false;}}, backgroundHandler);} catch (CameraAccessException | IllegalStateException e) {Log.e(TAG, "拍照过程异常: " + e.getClass().getSimpleName(), e);isCapturing = false;}}/*** 保存图片到媒体库*/private void saveImage(byte[] bytes) {Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);if (imageUri != null) {try (FileOutputStream output = (FileOutputStream) resolver.openOutputStream(imageUri)) {output.write(bytes);Log.i(TAG, "图像保存成功,大小: " + bytes.length + " bytes");runOnUiThread(() -> Toast.makeText(this, "图片已保存到相册", Toast.LENGTH_SHORT).show());} catch (IOException e) {Log.e(TAG, "保存文件失败: " + e.getMessage());}}}/*** 拍照前检查*/private boolean checkTakePicture() {if (cameraDevice == null) {Log.w(TAG, "拍照失败: 相机未初始化");return false;}if (cameraCaptureSession == null) {Log.e(TAG, "拍照错误: CameraCaptureSession为空");return false;}if (backgroundHandler == null) {Log.e(TAG, "拍照错误: backgroundHandler未初始化");startBackgroundThread();return false;}return true;}/*** 选择最优尺寸(匹配View比例)*/private Size chooseOptimalSize(Size[] choices, int viewWidth, int viewHeight) {List<Size> bigEnough = new ArrayList<>();float viewRatio = (float) viewWidth / viewHeight;for (Size option : choices) {float ratio = (float) option.getWidth() / option.getHeight();// 比例误差≤0.1,且尺寸不超过Viewif (Math.abs(ratio - viewRatio) <= 0.1 && option.getWidth() <= viewWidth && option.getHeight() <= viewHeight) {bigEnough.add(option);}}// 选择最大的合适尺寸if (!bigEnough.isEmpty()) {return Collections.max(bigEnough, new CompareSizesByArea());}// 无合适尺寸时返回第一个Log.w(TAG, "未找到完美匹配尺寸,使用默认");return choices[0];}/*** 尺寸比较器(按面积排序)*/static class CompareSizesByArea implements Comparator<Size> {@Overridepublic int compare(Size lhs, Size rhs) {return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());}}/*** 相机设备状态回调*/private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {@Overridepublic void onOpened(@NonNull CameraDevice camera) {Log.i(TAG, "相机已打开");cameraDevice = camera;createCameraPreviewSession(); // 相机打开后创建预览会话}@Overridepublic void onDisconnected(@NonNull CameraDevice camera) {Log.w(TAG, "相机断开连接");camera.close();cameraDevice = null;}@Overridepublic void onError(@NonNull CameraDevice camera, int error) {Log.e(TAG, "相机错误: " + error);camera.close();cameraDevice = null;}};// ---------------------- 生命周期方法 ----------------------@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "onResume —————————————————————