Android 11+(API30+)相机Intent无法正常工作问题排查
Android 11+ 相机拍照后无法加载图片,提示
resolveUri failed on bad bitmap uri 问题描述
基于Android官方《相机基础》教程实现的图片选择功能,支持图库与相机选择:
- 图库选择功能全版本正常运行
- 相机功能仅在API<30(Android<11)设备上正常
- API30+设备拍照后无法加载图片,日志输出提示:
W/ImageView: resolveUri failed on bad bitmap uri: - 已确认无权限缺失,尝试过Picasso库、Bitmap手动处理等方案均无效
复现代码
import android.app.Activity import android.content.Intent import android.net.Uri import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Environment import android.provider.MediaStore import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.FileProvider import simplyquotes.example.myapplication.databinding.ActivityMainBinding import java.io.File import java.io.IOException import java.text.SimpleDateFormat import java.util.* class MainActivity : AppCompatActivity() { private var _binding: ActivityMainBinding? = null private val binding get() = _binding!! private var currentPhotoUri: Uri = Uri.EMPTY private val intentLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == Activity.RESULT_OK) { val photoResult: Uri? = it.data?.data if(photoResult != null) { // 用户选择图库图片 this.contentResolver.takePersistableUriPermission( photoResult, Intent.FLAG_GRANT_READ_URI_PERMISSION ) currentPhotoUri = photoResult changeProfilePicture(currentPhotoUri) } else { // 用户拍照完成 changeProfilePicture(currentPhotoUri) } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) _binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.button.setOnClickListener { openIntentChooserForImageSources() } } @Throws(IOException::class) private fun createImageFile(): File { val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date()) val storageDir = this.getExternalFilesDir(Environment.DIRECTORY_PICTURES) val file = File.createTempFile( "JPEG_${timeStamp}_", /* 前缀 */ ".jpg", /* 后缀 */ storageDir /* 目录 */ ) currentPhotoUri = FileProvider.getUriForFile(this.applicationContext ,this.packageName, file) return file } private fun openIntentChooserForImageSources() { // 构建图库Intent val galleryIntent = Intent(Intent.ACTION_OPEN_DOCUMENT, MediaStore.Images.Media.INTERNAL_CONTENT_URI) // 构建相机Intent val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) cameraIntent.also { takePictureIntent -> takePictureIntent.resolveActivity(this.packageManager)?.also { val photoFile: File? = try { createImageFile() } catch (e: IOException){ null } photoFile?.also { val photoFileUri: Uri = FileProvider.getUriForFile( this.applicationContext, this.packageName, it ) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoFileUri) } } } val intentChooser = Intent.createChooser(galleryIntent, "Select an app") intentChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(cameraIntent)) intentLauncher.launch(intentChooser) } private fun changeProfilePicture(uri: Uri) { binding.imageView2.setImageURI(uri) } }
问题原因
- 权限传递失效:Android 11+的存储权限机制变更后,使用
Intent.createChooser同时启动多个Intent时,相机Intent的FLAG_GRANT_WRITE_URI_PERMISSION无法正确传递给相机应用,导致相机无法写入你通过FileProvider生成的Uri,最终图片文件未被正确创建,currentPhotoUri指向无效路径。 - 冗余Uri生成:
createImageFile中已经生成了currentPhotoUri,但相机Intent构建时又重复生成了一次Uri,虽然指向同一文件,但增加了权限传递的复杂度。
解决方案
1. 修正权限传递逻辑
修改openIntentChooserForImageSources方法,手动给相机应用授予Uri的读写权限,确保权限正确传递:
private fun openIntentChooserForImageSources() { // 图库Intent val galleryIntent = Intent(Intent.ACTION_OPEN_DOCUMENT, MediaStore.Images.Media.INTERNAL_CONTENT_URI) galleryIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // 相机Intent val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) var photoFileUri: Uri? = null val photoFile: File? = try { createImageFile() } catch (e: IOException) { null } photoFile?.also { photoFileUri = FileProvider.getUriForFile( this.applicationContext, "${this.packageName}.fileprovider", // 需与Manifest中FileProvider的authorities一致 it ) // 手动给所有能处理相机Intent的应用授予权限 val resInfoList = packageManager.queryIntentActivities(cameraIntent, android.content.pm.PackageManager.MATCH_DEFAULT_ONLY) for (resolveInfo in resInfoList) { val packageName = resolveInfo.activityInfo.packageName grantUriPermission(packageName, photoFileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION) } cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoFileUri) currentPhotoUri = photoFileUri!! // 统一赋值currentPhotoUri } // 构建Intent选择器,确保权限传递有效 val intentList = mutableListOf<Intent>() photoFileUri?.let { intentList.add(cameraIntent) } intentList.add(galleryIntent) val chooserIntent = Intent.createChooser(intentList.removeAt(0), "Select an app") chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentList.toTypedArray()) intentLauncher.launch(chooserIntent) }
2. 移除冗余Uri生成
修改createImageFile方法,不再内部生成Uri,简化逻辑:
@Throws(IOException::class) private fun createImageFile(): File { val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${timeStamp}_", ".jpg", storageDir ) }
3. 确保FileProvider配置正确
检查AndroidManifest.xml中的FileProvider配置:
<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> </application>
同时确保res/xml/file_paths.xml配置正确:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures" /> </paths>
验证效果
修改后,API30+设备拍照完成后,相机应用可正确写入图片文件,currentPhotoUri指向有效路径,ImageView能正常加载拍摄的图片。
内容的提问来源于stack exchange,提问作者JustSightseeing
相关产品推荐
相关产品推荐

