Android 11设备(三星机型为主)使用Camera Intent横屏拍照返回后崩溃问题
解决Android 11(三星设备)横屏拍照后崩溃的NPE问题
根据你描述的情况,这个崩溃的核心原因是横竖屏切换导致Fragment重建,之前存储的拍照目标文件路径丢失,加上Android 11(尤其是三星定制系统)对相机Intent返回结果的处理细节变化,最终在onActivityResult中调用new File()时传入了null值,触发NullPointerException。
下面是针对性的解决方案,完全适配Android 11+,同时兼容低版本:
1. 用ViewModel保存拍照文件对象(避免配置变更丢失)
横竖屏切换会触发Fragment/Activity重建,原来的实例变量会被重置。我们可以用ViewModel来持久化拍照的临时文件对象,确保重建后依然能获取到:
步骤1:创建CameraViewModel
import androidx.lifecycle.ViewModel import java.io.File class CameraViewModel : ViewModel() { var photoFile: File? = null }
步骤2:在Fragment中关联ViewModel
import androidx.fragment.app.viewModels private val cameraViewModel by viewModels<CameraViewModel>()
2. 重构拍照文件创建逻辑
按照官方文档的方式创建临时文件,但把文件对象存入ViewModel,而不是局部/实例变量:
import android.os.Environment import java.io.File import java.io.IOException import java.text.SimpleDateFormat import java.util.* private fun createImageFile(): File { val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) val storageDir = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES) return File.createTempFile( "JPEG_${timeStamp}_", ".jpg", storageDir ).apply { // 将文件对象存入ViewModel cameraViewModel.photoFile = this } }
3. 启动相机Intent(使用FileProvider)
确保用FileProvider生成Uri,适配Android 7+的文件权限要求:
import android.content.Intent import android.provider.MediaStore import androidx.core.content.FileProvider private const val REQUEST_IMAGE_CAPTURE = 65660 private fun dispatchTakePictureIntent() { Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent -> takePictureIntent.resolveActivity(requireContext().packageManager)?.also { val photoFile = try { createImageFile() } catch (ex: IOException) { // 处理文件创建失败的情况 null } photoFile?.also { val photoURI = FileProvider.getUriForFile( requireContext(), "${requireContext().packageName}.fileprovider", it ) takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI) startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE) } } } }
4. 修复onActivityResult的空值判断
从ViewModel中获取文件对象,避免空指针:
import android.app.Activity import android.graphics.BitmapFactory import android.widget.Toast override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == Activity.RESULT_OK) { // 优先从ViewModel获取文件,避免重建后为空 val photoFile = cameraViewModel.photoFile ?: run { Toast.makeText(requireContext(), "无法加载拍摄的图片", Toast.LENGTH_SHORT).show() return } // 将图片加载到ImageView val imageBitmap = BitmapFactory.decodeFile(photoFile.absolutePath) yourImageViewId.setImageBitmap(imageBitmap) } }
5. 确保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>
为什么这个方案能解决问题?
- Android 11及三星设备横屏拍照时,相机应用会导致你的Fragment重建,原来的文件路径变量会被清空,而ViewModel不受配置变更影响,能保留文件引用。
- 使用应用专属目录存储临时拍照文件,不需要额外的存储权限,规避了Android 11的存储访问限制。
- 提前做空值判断,避免
NullPointerException的触发。
内容的提问来源于stack exchange,提问作者Roshan
相关产品推荐
相关产品推荐

