Android P Camera2当SD卡被拔出来自动切换到内部存储

针对Camera2在SD卡拔出后仍提示无法访问的问题,本文介绍了一种改进方案,通过修改源代码中的存储空间更新函数和存储状态判断函数,实现在内部存储空间足够时自动切换至内部存储,提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求: Camera2 当拔出SD卡后,设备一直提示“无法访问SD卡”,体验不好
修改方法:依据现有流程中的当内部存储空间不够时候,切换到SD的方案修改

判断提示的函数为:packages/apps/SnapdragonCamera / src/com/android/camera/CameraActivity.java

    protected long updateStorageSpace() {
        synchronized (mStorageSpaceLock) {
            mStorageSpaceBytes = Storage.getAvailableSpace();
            if (Storage.switchSavePath()) {
                mStorageSpaceBytes = Storage.getAvailableSpace();
                mMainHandler.sendEmptyMessage(SWITCH_SAVE_PATH);
            } **else if (Storage.switchInternalSavePath()) {
                mStorageSpaceBytes = Storage.getAvailableSpace();
                mMainHandler.sendEmptyMessage(SWITCH_INTERNAL_SAVE_PATH);
            }**
            return mStorageSpaceBytes;
        }
    }

其判断函数为:packages/apps/SnapdragonCamera / src/com/android/camera/Storage.java

    public static boolean switchInternalSavePath() {
        if (isSaveSDCard()
                && getInternalStorageAvailableSpace() > LOW_STORAGE_THRESHOLD_BYTES
                && getSDCardAvailableSpace() == UNKNOWN_SIZE) {
            setSaveSDCard(false);
            return true;
        }
        return false;
    }

判断SD卡的状态后,然后就是切换SD卡的存储了

    private class MainHandler extends Handler {
        public MainHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == HIDE_ACTION_BAR) {
                removeMessages(HIDE_ACTION_BAR);
                CameraActivity.this.setSystemBarsVisibility(false);
            } else if ( msg.what == SWITCH_SAVE_PATH ) {
                mCurrentModule.onSwitchSavePath();
            } else if ( msg.what == SWITCH_INTERNAL_SAVE_PATH ) {
                mCurrentModule.onSwitchInternalSavePath();
            }
        }
    }

在查找器对应的实现函数接口mCurrentModule.onSwitchInternalSavePath()
packages/apps/SnapdragonCamera / src/com/android/camera/CaptureModule.java

    @Override
    public void onSwitchSavePath() {
        mSettingsManager.setValue(SettingsManager.KEY_CAMERA_SAVEPATH, "1");
        RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard,
                Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSwitchInternalSavePath() {
        mSettingsManager.setValue(SettingsManager.KEY_CAMERA_SAVEPATH, "0");
        RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_internal,
                Toast.LENGTH_SHORT).show();
    }

以上修改即可满足要求。

<think>嗯,用户这次需要的是关于AndroidCameraAPI2的具体Java实现代码。考虑到用户明确指向Java(而不是Kotlin),且需求聚焦在拍照功能的实现上,这应该是一个Android相机开发入门需求。之前对话历史里提到过系统级指令要求,特别要注意代码块格式和中文回答的要求。用户作为开发者显然需要可直接运行的示例,但CameraAPI2的复杂性决定了必须分步解释。从用户提问方式看,ta可能已经尝试过基础开发但在具体实现环节。需要重点解释三个核心对象:CameraDevice、CameraCaptureSession和CaptureRequest的协作关系,这是API2最容易被误解的部分。考虑到安全规范,必须包含动态权限申请的代码模板,否则用户在Android6.0+设备无法运行。同时提醒Camera2特有的特征:相机权限不仅需要manifest声明,运行时还要额外申请CAMERA权限和WRITE_EXTERNAL_STORAGE权限。硬件兼容性方面,需要特别说明API21最低版本要求,以及前置/后置摄像头选择方法。示例使用后置摄像头作为基准,但应注明getCameraCharacteristics的关键作用。拍摄流程中imageReader的处理是重点难点:照片完成回调的线程问题容易引发崩溃,必须强调使用Handler切换线程。示例里采用ImageSaver封装类处理耗时保存操作是较优方案。最后考虑延伸问题设计:焦点控制与预览回调都是实际开发中的高频痛点,自定义回调处理功能对高级用户也有参考价值。</think> 以下是使用Android Camera2 API实现拍照功能的Java示例代码,包含关键步骤解析: --- ### 1. 添加权限和特性声明 **AndroidManifest.xml** ```xml <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> <activity ... /> <!-- 声明相机特性 --> <uses-feature android:name="android.hardware.camera" /> </application> ``` --- ### 2. 实现拍照核心代码 ```java public class CameraActivity extends AppCompatActivity { private CameraDevice cameraDevice; private CameraCaptureSession captureSession; private ImageReader imageReader; private String cameraId; private Handler backgroundHandler; // 1. 初始化预览Surface private SurfaceTexture textureViewSurface; private TextureView textureView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); textureView = new TextureView(this); setContentView(textureView); // 2. 打开相机 CameraManager manager = (CameraManager) getSystemService(CAMERA_SERVICE); cameraId = manager.getCameraIdList()[0]; // 默认使用后置摄像头 // 3. 创建图片读取器(保存照片) imageReader = ImageReader.newInstance( 1920, 1080, ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener( reader -> saveImage(reader), null); } // 4. 相机准备就绪回调 private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraDevice = camera; startPreview(); // 开始预览 } // ... 省略错误处理 }; // 5. 启动相机预览 private void startPreview() { Surface previewSurface = new Surface(textureViewSurface); try { // 配置捕获会话 cameraDevice.createCaptureSession( Arrays.asList(previewSurface, imageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { captureSession = session; try { // 6. 设置持续预览请求 CaptureRequest.Builder builder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); builder.addTarget(previewSurface); session.setRepeatingRequest(builder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } }, backgroundHandler ); } catch (CameraAccessException e) { e.printStackTrace(); } } // 7. 拍照按钮点击事件 public void takePicture(View view) { if (cameraDevice == null) return; try { // 创建拍照请求 CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(imageReader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 8. 执行拍照 captureSession.capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(...) { Toast.makeText(CameraActivity.this, "照片已保存", Toast.LENGTH_SHORT).show(); } }, backgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } // 9. 保存图片到存储 private void saveImage(ImageReader reader) { Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] data = new byte[buffer.remaining()]; buffer.get(data); // 在子线程保存文件 AsyncTask.execute(() -> { File file = new File(getExternalFilesDir(null), "photo_" + System.currentTimeMillis() + ".jpg"); try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(data); } catch (IOException e) { e.printStackTrace(); } image.close(); }); } } ``` --- ### 关键步骤说明: 1. **相机权限动态申请**:Android 6.0+需运行时申请权限 2. **选择合适分辨率**:通过`StreamConfigurationMap`获取设备支持的分辨率 3. **焦点控制**:通过`CONTROL_AF_TRIGGER_START`实现自动对焦 4. **方向处理**:根据设备旋转角度设置`JPEG_ORIENTATION` 5. **生命周期管理**: ```java @Override protected void onPause() { closeCamera(); super.onPause(); } ``` --- ### 注意事项: 1. **相机ID选择**:使用`CameraCharacteristics.LENS_FACING_BACK`选择后置摄像头 2. **线程管理**:通过`HandlerThread`创建后台线程处理相机操作 3. **异常处理**:捕获`CameraAccessException`和`NullPointerException` 4. **兼容性检查**:使用`PackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)`检测设备是否支持相机 --- ### 延伸问题: 1. 如何处理多摄像头设备的镜头切换2. 如何实现手动对焦/曝光控制? 3. 预览画面如何实现适配不同幕比例? 4. Camera2和已废弃的Camera API有何核心区别? 5. 如何为拍照添加实时图像分析功能? > 建议参考官方文档:[Camera2 API Overview](https://developer.android.com/reference/android/hardware/camera2/package-summary) 和[相机开发指南](https://developer.android.com/guide/topics/media/camera)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值