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

Android Kotlin相机拍照触发TransactionTooLargeException问题求助

解决相机拍照时的TransactionTooLargeException异常

问题分析

你遇到的TransactionTooLargeException是因为Binder进程间通信的数据包大小超过了系统限制(通常约1MB),这里3MB的数据包已经远超阈值。从异常栈来看,是Activity在onStop阶段传递状态数据时触发的,大概率是Activity保存的状态里包含了大对象,或者相机返回的隐含数据过大。

解决方案

1. 清理Activity的状态保存逻辑

检查你的onSaveInstanceState方法,绝对不要在其中存储Bitmap、大字节数组或者复杂的大对象。如果之前有保存图片相关数据,改成存储文件Uri的字符串即可,恢复时通过Uri重新加载图片:

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    // 只存Uri字符串,不要存Bitmap或其他大对象
    imageUri?.let { outState.putString("IMAGE_URI", it.toString()) }
}

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    val uriStr = savedInstanceState.getString("IMAGE_URI")
    uriStr?.let { imageUri = Uri.parse(it) }
}

2. 确保相机拍照完全基于文件Uri(禁用缩略图返回)

不管用Intent还是ActivityResultContract,都要确保相机将照片直接写入你指定的文件,不要依赖相机返回的缩略图数据:

  • 完善ActivityResultContract的实现:你当前的Contract代码缺少创建文件Uri和启动的逻辑,正确写法应该和Intent方式一致,预先创建文件并传入Uri:
// 全局或成员变量存储Uri
private var imageUri: Uri? = null

private val takePictureContract = registerForActivityResult(ActivityResultContracts.TakePicture()) { success ->
    if (success) {
        imageUri?.let {
            // 加载图片时务必压缩,避免内存占用过大
            viewBinding.imgUserProfile.setImageURI(null) // 重置ImageView缓存
            viewBinding.imgUserProfile.setImageURI(it)
        }
    }
}

// 启动相机的方法
fun openCamera(activity: Activity) {
    val imgFile = createImageFile(activity)
    imageUri = FileProvider.getUriForFile(activity.applicationContext, authr, imgFile)
    takePictureContract.launch(imageUri)
}

// 复用你之前的createImageFile方法
@Throws(IOException::class)
private fun createImageFile(activity: Activity): File {
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    val imageFileName = "JPEG_$timeStamp_"
    val storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(imageFileName, ".jpg", storageDir)
}

3. 加载图片时强制压缩

拍照得到的原图尺寸通常很大,直接加载会占用大量内存,可能间接导致Activity状态数据膨胀。可以通过BitmapFactory压缩后再加载:

fun loadCompressedImage(uri: Uri, imageView: ImageView) {
    val options = BitmapFactory.Options().apply {
        inJustDecodeBounds = true
        // 先获取图片尺寸
        contentResolver.openInputStream(uri)?.use { BitmapFactory.decodeStream(it, null, this) }
        // 计算压缩比例,这里按ImageView的宽高缩小
        val reqWidth = imageView.width
        val reqHeight = imageView.height
        inSampleSize = calculateInSampleSize(this, reqWidth, reqHeight)
        inJustDecodeBounds = false
    }
    contentResolver.openInputStream(uri)?.use {
        val bitmap = BitmapFactory.decodeStream(it, null, options)
        imageView.setImageBitmap(bitmap)
    }
}

private fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    val (height: Int, width: Int) = options.run { outHeight to outWidth }
    var inSampleSize = 1

    if (height > reqHeight || width > reqWidth) {
        val halfHeight = height / 2
        val halfWidth = width / 2
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }
    return inSampleSize
}

4. 避免在Activity间传递大数据

绝对不要通过Intent的extra传递Bitmap或大文件的字节数组,所有图片操作都基于文件Uri完成,这样数据不会经过Binder通道,从根源避免TransactionTooLargeException。

内容的提问来源于stack exchange,提问作者Waseem

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 22:48:34