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
相关产品推荐
相关产品推荐

