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

Android 8.0一加3拍照应用崩溃,Android 6.0模拟器正常求助

解决Android 8.0设备拍照崩溃问题(兼容Android 6.0+)

看起来你遇到的是Android版本兼容性问题,尤其是Android 7.0+的文件权限限制和相机返回数据的不确定性导致的崩溃,我来帮你一步步解决:

1. 核心问题分析

你的代码在Android 6.0模拟器正常,但在Android 8.0真机崩溃,主要有两个原因:

  • Android 7.0+的FileUriExposedException:系统禁止应用直接向其他应用暴露file://格式的Uri,否则会抛出权限异常;
  • 相机返回数据不可靠:部分相机应用(尤其是高版本系统的)不会将缩略图放入data.getExtras()中,甚至data本身可能为null,直接调用会触发空指针异常。

2. 解决方案:改用FileProvider指定拍照输出路径

我们需要通过FileProvider生成安全的Uri,让相机应用将照片保存到我们指定的位置,同时避免依赖不可靠的返回数据。

步骤1:注册FileProvider到Manifest

AndroidManifest.xml<application>标签内添加以下配置:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

步骤2:创建文件路径配置文件

res/xml目录下新建file_paths.xml(如果没有xml目录就创建一个),内容如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 指定应用外部存储的Pictures目录作为照片保存路径 -->
    <external-files-path name="captured_images" path="Pictures" />
</paths>

步骤3:修改相机Intent启动代码

替换原来的拍照Intent代码,改为指定输出文件的方式:

private File mCapturedPhotoFile; // 保存拍照后的文件引用

private void startCamera() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    // 先确认有相机应用能处理这个Intent
    if (takePictureIntent.resolveActivity(getPackageManager()) == null) {
        Toast.makeText(this, "未找到可用相机应用", Toast.LENGTH_SHORT).show();
        return;
    }

    try {
        // 创建临时照片文件
        mCapturedPhotoFile = createTempPhotoFile();
    } catch (IOException e) {
        e.printStackTrace();
        Toast.makeText(this, "创建照片文件失败", Toast.LENGTH_SHORT).show();
        return;
    }

    // 生成安全的FileProvider Uri
    Uri photoUri = FileProvider.getUriForFile(
            this,
            BuildConfig.APPLICATION_ID + ".fileprovider", // 和Manifest中的authorities一致
            mCapturedPhotoFile
    );
    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
    startActivityForResult(takePictureIntent, TAKE_IMAGE_REQUEST);
}

// 创建临时照片文件的工具方法
private File createTempPhotoFile() throws IOException {
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    String fileName = "CAPTURE_" + timeStamp + "_";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    return File.createTempFile(fileName, ".jpg", storageDir);
}

步骤4:重构onActivityResult逻辑

不再依赖返回的data,直接使用我们创建的mCapturedPhotoFile路径:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == TAKE_IMAGE_REQUEST && resultCode == RESULT_OK) {
        if (mCapturedPhotoFile == null || !mCapturedPhotoFile.exists()) {
            Toast.makeText(this, "照片获取失败", Toast.LENGTH_SHORT).show();
            return;
        }

        // 直接使用照片文件的绝对路径
        String photoPath = mCapturedPhotoFile.getAbsolutePath();
        try {
            Img img = crearImg(photoPath);
            if (user.isOnline()) {
                Img.uploadImage(NoteActivity.this, img);
            }
        } catch (Exception e) {
            e.printStackTrace(); // 建议打印异常,方便排查问题
            Toast.makeText(this, "处理照片失败", Toast.LENGTH_SHORT).show();
        }
    }
}

3. 补充:动态权限申请

如果你的应用targetSdkVersion >= 23,还需要动态申请相机和存储权限:

private static final int REQUEST_CAMERA_PERMISSIONS = 101;

// 在启动相机前先检查权限
private void checkPermissionsThenStartCamera() {
    String[] requiredPermissions = {
            Manifest.permission.CAMERA,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    boolean allGranted = true;
    for (String permission : requiredPermissions) {
        if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            allGranted = false;
            break;
        }
    }

    if (allGranted) {
        startCamera();
    } else {
        ActivityCompat.requestPermissions(this, requiredPermissions, REQUEST_CAMERA_PERMISSIONS);
    }
}

// 处理权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA_PERMISSIONS) {
        boolean allGranted = true;
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                allGranted = false;
                break;
            }
        }
        if (allGranted) {
            startCamera();
        } else {
            Toast.makeText(this, "需要相机和存储权限才能拍照", Toast.LENGTH_SHORT).show();
        }
    }
}

最后,记得把原来的启动相机代码替换成调用checkPermissionsThenStartCamera(),而不是直接启动Intent。

这样修改后,既解决了Android 8.0的权限问题,又避免了依赖不可靠的相机返回数据,应该能解决你的崩溃问题。

内容的提问来源于stack exchange,提问作者Artur Alcoverro Pastó

火山引擎 最新活动