You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Fragment中拍照显示至ImageView遇TransactionTooLargeException问题求助

解决Android Fragment+Navigation相机集成的TransactionTooLargeException问题

错误原因

TransactionTooLargeException触发的核心原因是:Activity停止时,Fragment的状态数据(如Bundle中存储的Bitmap)超过了Binder事务的大小限制(约1MB)。你调用的getResizedBitmap()虽能压缩图片,但如果Bitmap被保存在Fragment的状态Bundle中(Navigation组件会自动处理状态保存),依然会触发异常。另外,你直接修改Bitmap宽高的photo!!.height = 150和photo!!.width = 120完全无效——因为Bitmap是不可变对象,这类代码不会改变图片尺寸,还可能引发潜在问题。

解决方案

1. 移除无效代码

直接删掉photo!!.height = 150和photo!!.width = 120两行无意义代码。

2. 改用指定文件路径拍照

ACTION_IMAGE_CAPTURE通过Intent返回的缩略图不仅尺寸有限,还会增加Intent的数据体积。正确做法是指定图片保存的本地文件路径,拍照后直接从文件读取图片,避免通过Intent传递Bitmap数据。

3. 状态保存时存储文件路径而非Bitmap

Fragment重建时,通过存储的文件路径重新加载图片,避免把Bitmap序列化到Bundle中,从根源上减少状态数据的体积。

修改后的完整代码示例

private lateinit var photoFile: File
private val CAMERA_REQUEST = 100
private val MY_CAMERA_PERMISSION_CODE = 200

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    binding.clickPhotoImageview.setOnClickListener {
        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED) {
            ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.CAMERA), MY_CAMERA_PERMISSION_CODE)
        } else {
            dispatchTakePictureIntent()
        }
    }

    // Fragment重建时加载之前保存的图片
    savedInstanceState?.getString("PHOTO_PATH")?.let { path ->
        val bitmap = getResizedBitmap(BitmapFactory.decodeFile(path), 200)
        binding.clickPhotoImageview.setImageBitmap(bitmap)
    }
}

private fun dispatchTakePictureIntent() {
    Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
        // 创建保存图片的临时文件
        photoFile = createImageFile()
        photoFile.also {
            val photoURI = FileProvider.getUriForFile(
                requireContext(),
                "${requireContext().packageName}.fileprovider",
                it
            )
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
            startActivityForResult(takePictureIntent, CAMERA_REQUEST)
        }
    }
}

private fun createImageFile(): File {
    val timeStamp: String = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
    val storageDir: File? = requireContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(
        "JPEG_${timeStamp}_",
        ".jpg",
        storageDir
    )
}

@Deprecated("Deprecated in Java")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == MY_CAMERA_PERMISSION_CODE) {
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(requireContext(), "camera permission granted", Toast.LENGTH_LONG).show()
            dispatchTakePictureIntent()
        } else {
            Toast.makeText(requireContext(), "camera permission denied", Toast.LENGTH_LONG).show()
        }
    }
}

@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == CAMERA_REQUEST && resultCode == Activity.RESULT_OK) {
        // 从本地文件加载图片并压缩
        val bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
        val resizedBitmap = getResizedBitmap(bitmap, 200)
        binding.clickPhotoImageview.setImageBitmap(resizedBitmap)
    }
}

fun getResizedBitmap(image: Bitmap, maxSize: Int): Bitmap? {
    var width = image.width
    var height = image.height
    val bitmapRatio = width.toFloat() / height.toFloat()
    if (bitmapRatio > 1) {
        width = maxSize
        height = (width / bitmapRatio).toInt()
    } else {
        height = maxSize
        width = (height * bitmapRatio).toInt()
    }
    return Bitmap.createScaledBitmap(image, width, height, true)
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    // 保存图片文件路径而非Bitmap
    if (::photoFile.isInitialized) {
        outState.putString("PHOTO_PATH", photoFile.absolutePath)
    }
}

额外配置说明

需要在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>

内容的提问来源于stack exchange,提问作者Jajatee Nandinee Sahoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:35:33