You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android Camera权限申请致应用启动崩溃问题求助

解决Android相机权限授予后首次启动崩溃问题

看起来你遇到的是权限申请异步流程与相机初始化时机不匹配导致的崩溃——首次启动时,你可能在权限还没拿到的情况下就尝试初始化相机预览,弹出权限弹窗并授予后,之前的初始化逻辑已经因为权限缺失失败了,而且没有重新触发初始化,最终导致相机相关组件未就绪引发崩溃;第二次打开时权限已经存在,初始化能正常执行,所以没问题。

下面是具体的解决步骤和代码示例:

一、核心问题排查

先对照你的Logcat崩溃日志,大概率会是这类错误:

java.lang.SecurityException: Permission Denial: opening camera ... without permission
或者
java.lang.NullPointerException: Attempt to invoke virtual method '...' on a null object reference

这说明相机初始化代码在权限未授予时就执行了,且权限授予后没有重新执行初始化。

二、正确的权限处理流程

1. 推荐使用Jetpack Activity Result API(AndroidX)

现在官方推荐用这种方式替代旧的onRequestPermissionsResult,逻辑更清晰:

首先在CameraActivity里定义权限请求的Contract:

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;

public class CameraActivity extends AppCompatActivity {
    // 相机权限请求Launcher
    private ActivityResultLauncher<String> requestCameraPermissionLauncher;
    // 相机预览相关对象
    private CameraManager cameraManager;
    private String cameraId;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);

        // 初始化权限请求Launcher
        requestCameraPermissionLauncher = registerForActivityResult(
                new ActivityResultContracts.RequestPermission(),
                isGranted -> {
                    if (isGranted) {
                        // 权限授予成功,初始化相机预览
                        initCameraPreview();
                    } else {
                        // 权限被拒绝,给出友好提示
                        Toast.makeText(this, "需要相机权限才能使用预览功能", Toast.LENGTH_SHORT).show();
                    }
                });

        // 启动时先检查权限状态
        checkCameraPermission();
    }

    private void checkCameraPermission() {
        if (checkSelfPermission(Manifest.permission.CAMERA)
                == PackageManager.PERMISSION_GRANTED) {
            // 已有权限,直接初始化预览
            initCameraPreview();
        } else {
            // 无权限,发起权限申请
            requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA);
        }
    }

    // 相机预览初始化逻辑,把原来的相机打开、预览绑定等代码统一放到这里
    private void initCameraPreview() {
        try {
            cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
            // 获取后置相机ID(根据需求调整为前置)
            cameraId = cameraManager.getCameraIdList()[0];
            // 绑定SurfaceView用于预览
            SurfaceView surfaceView = findViewById(R.id.surface_view);
            surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    try {
                        // 打开相机并启动预览
                        cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
                            @Override
                            public void onOpened(CameraDevice camera) {
                                startCameraPreview(camera, holder);
                            }

                            @Override
                            public void onDisconnected(CameraDevice camera) {
                                camera.close();
                            }

                            @Override
                            public void onError(CameraDevice camera, int error) {
                                camera.close();
                                Toast.makeText(CameraActivity.this, "相机启动失败", Toast.LENGTH_SHORT).show();
                            }
                        }, null);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    // 可在这里调整预览尺寸适配屏幕
                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                    // 释放相机资源
                    if (cameraManager != null) {
                        // 补充相机关闭逻辑
                    }
                }
            });
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private void startCameraPreview(CameraDevice camera, SurfaceHolder holder) {
        // 这里根据Camera2 API实现具体的预览逻辑(创建CaptureSession、CaptureRequest等)
        // 如果用的是Camera1 API,替换为对应的初始化代码即可
    }
}

2. 如果使用旧版onRequestPermissionsResult(兼容非AndroidX)

如果你还在用传统的权限回调,确保在权限授予后重新初始化相机:

private static final int CAMERA_PERMISSION_REQUEST_CODE = 1001;

// 启动时检查权限的逻辑
private void checkCameraPermission() {
    if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
        requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_PERMISSION_REQUEST_CODE);
    } else {
        initCameraPreview();
    }
}

// 权限回调处理
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == CAMERA_PERMISSION_REQUEST_CODE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 权限授予,重新初始化相机预览
            initCameraPreview();
        } else {
            Toast.makeText(this, "相机权限被拒绝,无法使用预览功能", Toast.LENGTH_SHORT).show();
        }
    }
}

三、关键注意事项

  • 严格控制初始化时机:相机打开、预览绑定等操作必须放到权限确认之后(无论是首次检查通过,还是权限申请回调成功),绝对不能在权限未明确时提前执行。
  • 处理权限拒绝场景:如果用户拒绝权限,要给出友好提示,避免直接崩溃。
  • 及时释放资源:在Activity的onPauseonDestroy方法里记得关闭相机、释放相关资源,避免内存泄漏和后续启动的权限冲突。
  • 检查硬件可用性:初始化前可以先检查设备是否有相机硬件,避免因无相机导致的崩溃。

四、验证解决效果

修改代码后,首次启动应用:

  1. 弹出权限申请弹窗,授予权限后会自动触发initCameraPreview方法,正常初始化相机预览,不会崩溃。
  2. 如果拒绝权限,会显示提示,不会执行初始化逻辑。
  3. 第二次启动时,因为权限已存在,会直接初始化预览,正常运行。

内容的提问来源于stack exchange,提问作者Bendo

火山引擎 最新活动