Android应用点击图片选择按钮崩溃问题排查求助
点击图库/相机按钮后应用崩溃问题分析与解决
崩溃日志
java.lang.RuntimeException: Could not copy bitmap to parcel blob. at android.graphics.Bitmap.nativeWriteToParcel(Native Method) at android.graphics.Bitmap.writeToParcel(Bitmap.java:2157) at android.os.Parcel.writeParcelable(Parcel.java:2538) at android.os.Parcel.writeValue(Parcel.java:2439) at android.os.Parcel.writeValue(Parcel.java:2316) at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) at android.os.Bundle.writeToParcel(Bundle.java:1362) at android.os.Parcel.writeBundle(Parcel.java:1334) at android.os.Parcel.writeValue(Parcel.java:2433) at android.os.Parcel.writeValue(Parcel.java:2323) at android.os.Parcel.writeSparseArray(Parcel.java:1422) at android.os.Parcel.writeValue(Parcel.java:2463) at android.os.Parcel.writeValue(Parcel.java:2316) at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) at android.os.Bundle.writeToParcel(Bundle.java:1362) at android.os.Parcel.writeBundle(Parcel.java:1334) at android.os.Parcel.writeValue(Parcel.java:2433) at android.os.Parcel.writeValue(Parcel.java:2323) at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) at android.os.Bundle.writeToParcel(Bundle.java:1362) at android.os.Parcel.writeBundle(Parcel.java:1334) at android.os.Parcel.writeValue(Parcel.java:2433) at android.os.Parcel.writeValue(Parcel.java:2323) at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) at android.os.Bundle.writeToParcel(Bundle.java:1362) at android.os.Parcel.writeBundle(Parcel.java:1334) at android.os.Parcel.writeValue(Parcel.java:2433) at android.os.Parcel.writeValue(Parcel.java:2323) at android.os.Parcel.writeArrayMapInternal(Parcel.java:1265) at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1802) at android.os.Bundle.writeToParcel(Bundle.java:1362) at android.os.Parcel.writeTypedObject(Parcel.java:2157) at android.app.IActivityClientController$Stub$Proxy.activityStopped(IActivityClientController.java:1257) at android.app.ActivityClient.activityStopped(ActivityClient.java:99) at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:143) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:210) at android.os.Looper.loop(Looper.java:299) at android.app.ActivityThread.main(ActivityThread.java:8247) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:559) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:954)
应用场景与问题详情
应用核心功能是收集用户输入(字符串/数值+多张图片Uri)并构建Record实例,该实例包含8个不同类型的参数,每个参数带有多个属性及唯一图片Uri数组。用户输入分3个Fragment完成:
- Fragment1收集3个参数,构建
Record(A,B,C) - Fragment2通过setters补充2个参数,得到
Record(A,B,C,D),此阶段无崩溃,Record.toString()输出符合预期 - Fragment3点击图库/相机按钮时直接崩溃
额外信息:
- 应用内存占用最高250MB
- 模拟器中无崩溃,但多款现代Android真机上均出现崩溃
- 已尝试使用ViewModel,未解决问题
相关代码片段
if (result.getResultCode() == Activity.RESULT_OK) { Intent data = result.getData(); if (data != null && data.getExtras() != null) { Bitmap imageBitmap = (Bitmap) data.getExtras().get("data"); if (imageBitmap != null) { Uri capturedImageUri = getImageUri(requireContext(), imageBitmap); handleGalleryOrCameraResult(capturedImageUri, requestCode); } else { // Handle when the imageBitmap is null } } } private void handleGalleryOrCameraResult(Uri selectedImageUri,int requestCode) { // Use the appropriate list based on the current scenario List<Uri> imageList; TextView imageCounter; RecyclerView recyclerView; switch (requestCode) { case 7: // Container imageList = DamageGoodImageUris; imageCounter = DamageGoodImageCounter; recyclerView = DamageGoodRecyclerView; break; case 8: // Lock imageList = HeatImageUris; imageCounter = HeatImageCounter; recyclerView = HeatRecyclerView; break; default: return; } // Add the selected image URI to the respective model's image list if (selectedImageUri != null) { imageList.add(selectedImageUri); } // Update the RecyclerView with the new image list setupRecyclerView(recyclerView, imageList, imageCounter); } // Helper method to get Uri from Bitmap private Uri getImageUri(Context context, Bitmap bitmap) { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes); String path = MediaStore.Images.Media.insertImage(context.getContentResolver(), bitmap, "Title", null); return Uri.parse(path); }
崩溃原因分析
- 核心原因:Bitmap体积过大,导致Activity进入后台(启动相机/图库时),系统尝试将Activity状态写入Parcel保存时,Bitmap超出Parcel的大小限制,触发序列化失败。从调用栈可见,崩溃发生在
activityStopped阶段,系统正在序列化Activity状态。 - 内存占用说明:250MB在现代Android设备上属于偏高但未达异常阈值,但如果
Record或Fragment/Activity中持有Bitmap强引用,会加剧内存压力,触发序列化问题。 - 代码隐患:当前代码将相机返回的Bitmap转存为Uri的操作冗余,且如果
Record或相关对象中持有Bitmap引用,会直接导致序列化失败。
解决方案
1. 移除Record及状态中的Bitmap引用
确保Record实例(或任何需要序列化的对象)只保存图片Uri,绝对不要持有Bitmap对象。所有图片操作均通过Uri加载,避免直接持有大内存对象。
2. 优化相机/图库图片获取逻辑
放弃通过Bitmap中转的方式,直接获取原始图片Uri:
- 相机:提前创建临时Uri,启动相机时指定输出Uri,直接使用该Uri即可,无需处理Bitmap
- 图库:直接获取
result.getData()返回的Uri,无需转存
示例优化代码:
// 创建相机临时Uri private Uri createCameraTempUri() { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.TITLE, "Camera Capture"); values.put(MediaStore.Images.Media.DESCRIPTION, "From App Camera"); return requireContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } // 启动相机 Uri cameraUri = createCameraTempUri(); Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri); startActivityForResult(cameraIntent, CAMERA_REQUEST_CODE); // 处理结果 if (result.getResultCode() == Activity.RESULT_OK) { if (requestCode == CAMERA_REQUEST_CODE) { handleGalleryOrCameraResult(cameraUri, requestCode); } else if (requestCode == GALLERY_REQUEST_CODE) { Uri galleryUri = result.getData(); if (galleryUri != null) { handleGalleryOrCameraResult(galleryUri, requestCode); } } }
3. 内存优化与引用清理
- 使用Glide/Picasso等图片加载库加载RecyclerView图片,自动处理Bitmap缓存与回收
- 在Fragment销毁时,清空图片列表引用,确保无用Bitmap能被GC回收
- 检查ViewModel中是否持有Bitmap引用,替换为Uri
内容的提问来源于stack exchange,提问作者CodingHell2312x
相关产品推荐
相关产品推荐

