为何调用系统相机拍照后返回的extra为null?附相关代码
这是Android开发里非常常见的一个坑,我来给你拆解原因和解决方案:
首先,你当前的代码用了MediaStore.ACTION_IMAGE_CAPTURE但没有指定图片的输出路径——问题就出在这。ACTION_IMAGE_CAPTURE的行为并没有统一的强制标准,不同厂商的系统相机应用实现差异很大:有些老版本的相机会把缩略图Bitmap放进返回Intent的"data" extra里,但很多现代相机应用(尤其是Android 6.0以上的系统),如果没有提前指定图片要保存到哪里,拍完后根本不会往返回的Intent里塞任何数据,自然就会出现data或者其extra为null的情况。
而且就算有些相机返回了"data",那也只是个小尺寸的缩略图,根本不适合用来做后续的图片处理(比如上传、编辑),所以正确的做法从来都不是依赖这个返回的extra,而是提前指定拍照输出的文件路径。
下面是具体的修复步骤,直接替换你的代码就行:
第一步:创建保存图片的文件和Uri
我们需要先在本地生成一个空文件,用来存储拍照后的完整图片,同时根据Android版本获取对应的Uri(Android 7.0及以上必须用FileProvider,不能直接用file://格式的Uri,否则会触发FileUriExposedException)。
添加这几个函数到你的Activity里:
private var currentPhotoPath: String? = null // 创建临时图片文件 private fun createImageFile(): File { val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${timeStamp}_", // 文件名前缀 ".jpg", // 文件后缀 storageDir // 存储目录(应用私有外部存储,不需要权限) ).apply { currentPhotoPath = absolutePath // 保存文件路径,后续用来访问图片 } } // 获取图片的Uri private fun getPhotoUri(): Uri { val photoFile = createImageFile() return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Android 7.0+ 使用FileProvider FileProvider.getUriForFile( this, "${packageName}.fileprovider", // 这个要和后续Manifest里的配置一致 photoFile ) } else { // 低版本直接用Uri.fromFile Uri.fromFile(photoFile) } }
第二步:修改拍照Intent,指定输出Uri
把你原来的拍照代码改成这样,确保Intent里带上EXTRA_OUTPUT参数:
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) // 先检查有没有相机应用能处理这个Intent takePictureIntent.resolveActivity(packageManager)?.also { val photoUri = getPhotoUri() takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri) startActivityForResult(takePictureIntent, TAKE_PICTURE) }
第三步:在onActivityResult中获取图片
这时候返回的data很可能还是null,但没关系,我们可以直接用之前保存的currentPhotoPath来拿到完整的图片文件:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == TAKE_PICTURE && resultCode == RESULT_OK) { currentPhotoPath?.let { imagePath -> val imageFile = File(imagePath) // 把File传给你的presenter就行,不用再依赖data里的extra了 presenter?.takeImg(this, imageFile) // 可选操作:把图片添加到系统媒体库,让相册能找到它 galleryAddPic() } } } // 把图片插入系统媒体库的工具函数 private fun galleryAddPic() { Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE).also { mediaScanIntent -> val imageFile = File(currentPhotoPath) mediaScanIntent.data = Uri.fromFile(imageFile) sendBroadcast(mediaScanIntent) } }
第四步:配置FileProvider(Android 7.0+必备)
最后需要在AndroidManifest.xml里添加FileProvider的配置,不然7.0以上系统会报错:
<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目录就自己创建),内容如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 对应我们之前用的getExternalFilesDir(Environment.DIRECTORY_PICTURES) --> <external-files-path name="my_images" path="Pictures" /> </paths>
这样改完之后,不管哪个厂商的相机应用,拍完照后图片都会保存到我们指定的文件里,你可以直接通过文件路径获取完整尺寸的图片,再也不用担心data或者extra为null的问题了。
内容的提问来源于stack exchange,提问作者D T

