Android Marshmallow相机拍照功能异常,其余版本正常技术求助
嘿,我看你遇到了个挺头疼的问题——相机拍照逻辑在除Android 6.0(Marshmallow,API 23)之外的设备都正常,唯独这个版本出问题,而且你已经排查过常规环节了。结合你给的代码片段,我大概率能定位到几个Marshmallow特有的坑,咱一步步来捋:
1. 权限判断的逻辑漏洞
Marshmallow是Android第一个引入运行时权限的版本,你的代码里有个关键判断:if (Build.VERSION.SDK_INT > M)——这里的M就是API23,也就是说Marshmallow本身会走到else分支,但这个版本恰恰需要严格的运行时权限检查!
如果你的PermissionManager.userHasPermission方法没有专门适配API23的权限校验逻辑,那很可能在Marshmallow上,你以为权限已经授予了,但实际上用户并没有在运行时同意,直接调用相机就会静默失败。
2. 媒体库刷新的兼容性问题
就算拍照成功保存了文件,Marshmallow对媒体库的更新机制和之前版本不一样——如果你只是把图片写到文件夹里,不主动通知媒体库扫描,图库的“最近”文件夹根本不会显示这张新图。这是很多开发者容易忽略的点!
3. 代码分支的逻辑错误
你代码里把API>23的情况导向了GetImageFromGallery(),但API23本身走else分支——如果else分支里的拍照逻辑没有适配Marshmallow的权限和文件规则,比如直接用File Uri而不是FileProvider,就会触发FileUriExposedException,导致拍照失败。
具体修复建议
先把权限逻辑掰正
首先要把权限判断和申请的逻辑统一适配所有版本,尤其是Marshmallow:
public void getImage() { final PermissionManager permissionManager = new PermissionManager(); if (permissionManager.userHasPermission(Addfood_items.this.getContext())) { // 权限没问题,直接走拍照逻辑 takePhoto(); } else { // 主动申请相机+存储权限(Marshmallow及以上必须这么做) ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1001); // 自定义请求码 } } // 别忘了处理权限申请的回调 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1001) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { takePhoto(); } else { Toast.makeText(this, "需要相机权限才能拍照哦", Toast.LENGTH_SHORT).show(); } } }
拍照后一定要通知媒体库
保存图片之后,必须调用MediaScannerConnection来刷新媒体库,这样图库才能及时看到新图片:
// 假设你已经把图片保存到了imageFile这个文件里 MediaScannerConnection.scanFile(getContext(), new String[]{imageFile.getAbsolutePath()}, new String[]{"image/jpeg"}, (path, uri) -> { // 扫描完成,这里可以做后续操作,比如更新UI });
用FileProvider避免Uri暴露问题
Marshmallow及以上版本不允许直接暴露File类型的Uri给其他应用(比如相机),必须用FileProvider来生成安全的Uri:
// 先创建保存图片的临时文件 private File createImageFile() throws IOException { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); return File.createTempFile( imageFileName, /* 前缀 */ ".jpg", /* 后缀 */ storageDir /* 存储目录 */ ); } // 启动相机的逻辑 private void takePhoto() { try { File imageFile = createImageFile(); Uri imageUri = FileProvider.getUriForFile(this, "com.your.app.package.fileprovider", // 要和你Manifest里的一致 imageFile); Intent takePicIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); takePicIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); // 给相机临时的写入权限 takePicIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); startActivityForResult(takePicIntent, 1002); } catch (IOException e) { e.printStackTrace(); Toast.makeText(this, "创建图片文件失败", Toast.LENGTH_SHORT).show(); } }
最后建议你在Marshmallow设备上打开Logcat,看看有没有权限拒绝、文件写入失败或者Uri暴露的异常日志,这些日志能帮你快速锁定具体问题!
内容的提问来源于stack exchange,提问作者sam

