Xamarin Essentials MediaPicker CapturePhotoAsync拍照后应用静默重启问题求助
Xamarin Essentials MediaPicker 拍照后静默崩溃修复方案
问题特性
- 仅在部分设备(典型如小米Redmi Note 9S)上复现,本地测试环境无异常
- 拍照点击确认后应用直接重启,AppCenter无任何异常日志上报
- 属于系统级低内存杀进程场景,不会触发应用层的异常捕获逻辑
核心原因
- 原生MediaPicker在小米定制ROM上存在已知内存泄漏问题,调用拍照后会占用大量系统RAM
- 现有图片处理逻辑存在严重内存浪费:同一份原图流被读取3次,Bitmap对象未主动释放,大内存数组未及时回收,短时间内内存占用峰值超过小米系统的进程内存阈值,被系统强制杀死
修复方案
1. 优化图片处理内存占用
核心优化点
- 仅读取一次原图流,缓存为字节数组后所有操作复用该数组,避免重复加载大文件
- 所有Bitmap对象包裹在
using块中,用完自动回收资源 - 大字节数组处理完后主动置空,配合手动触发GC及时释放内存
- 增加EXIF读取异常兜底,避免元数据解析失败导致崩溃
优化后核心代码
// 优化后的LoadPhotoAsync方法 async Task LoadPhotoAsync(FileResult photo) { if (photo == null) { PhotoPath = null; return; } // 仅加载一次原图数据 byte[] originalImageBytes; using (var stream = await photo.OpenReadAsync()) { originalImageBytes = ReadFully(stream); } // 读取EXIF旋转信息 int rotationAngle = 90; try { using var exifStream = new MemoryStream(originalImageBytes); var meta = ImageMetadataReader.ReadMetadata(exifStream); var subIfd0Directory = meta.OfType<ExifIfd0Directory>().FirstOrDefault(); var orientation = subIfd0Directory?.GetDescription(ExifDirectoryBase.TagOrientation); rotationAngle = GetRotationAngle(orientation); } catch (Exception ex) { var properties = new Dictionary<string, string> { { "Função:", "LoadPhotoAsync" }, { "Arquivo:", "VisitDetailsRegistrationViewModel"}, { "Observação", "EXIF读取异常"} }; Crashes.TrackError(ex, properties); } // 处理原图保存 var newFile = Path.Combine(FileSystem.CacheDirectory, photo.FileName); var rotatedImageBytes = RotateImage(originalImageBytes, rotationAngle, 92); using (var saveStream = new MemoryStream(rotatedImageBytes)) using (var fileStream = File.OpenWrite(newFile)) { await saveStream.CopyToAsync(fileStream); } // 保存到图库 using (var galleryStream = new MemoryStream(rotatedImageBytes)) { try { await MediaGallery.SaveAsync(MediaFileType.Image, galleryStream, photo.FileName); } catch (Exception ex) { var properties = new Dictionary<string, string> { { "Função:", "LoadPhotoAsync" }, { "Arquivo:", "VisitDetailsRegistrationViewModel"}, { "Observação", "保存到图库异常"} }; Crashes.TrackError(ex, properties); } } rotatedImageBytes = null; PhotoPath = newFile; // 处理缩略图 var thumbFile = Path.Combine(FileSystem.CacheDirectory, "Thumb-" + photo.FileName); var resizedThumbBytes = ResizeImage(originalImageBytes, 500, 500, 92); var rotatedThumbBytes = RotateImage(resizedThumbBytes, rotationAngle, 92); resizedThumbBytes = null; using (var thumbStream = new MemoryStream(rotatedThumbBytes)) using (var thumbFileStream = File.OpenWrite(thumbFile)) { await thumbStream.CopyToAsync(thumbFileStream); } // 主动释放所有大内存资源 rotatedThumbBytes = null; originalImageBytes = null; GC.Collect(); ThumbPath = thumbFile; } // 优化后的RotateImage方法,自动回收Bitmap资源 public static byte[] RotateImage(byte[] imageData, float degrees, int quality) { using Bitmap originalImage = BitmapFactory.DecodeByteArray(imageData, 0, imageData.Length); using var matrix = new Matrix(); matrix.PostRotate(degrees); using Bitmap rotatedBitmap = Bitmap.CreateBitmap(originalImage, 0, 0, originalImage.Width, originalImage.Height, matrix, true); using var ms = new MemoryStream(); rotatedBitmap.Compress(Bitmap.CompressFormat.Jpeg, quality, ms); return ms.ToArray(); } // 优化后的ResizeImage方法,自动回收Bitmap资源 public static byte[] ResizeImage(byte[] imageData, float width, float height, int quality) { using Bitmap originalImage = BitmapFactory.DecodeByteArray(imageData, 0, imageData.Length); float scaleFactor = originalImage.Width > originalImage.Height ? width / originalImage.Width : height / originalImage.Height; int newWidth = (int)(originalImage.Width * scaleFactor); int newHeight = (int)(originalImage.Height * scaleFactor); using Bitmap resizedImage = Bitmap.CreateScaledBitmap(originalImage, newWidth, newHeight, false); using var ms = new MemoryStream(); resizedImage.Compress(Bitmap.CompressFormat.Jpeg, quality, ms); return ms.ToArray(); }
2. 系统配置适配
在Android项目的AndroidManifest.xml的application标签中添加android:largeHeap="true"配置,允许应用申请更大的内存空间,降低被系统低内存杀死的概率。
3. 组件替换(可选)
如果优化后仍然有复现,可将Xamarin Essentials的MediaPicker替换为第三方Xam.Plugin.Media库,该库对国产定制ROM的兼容性更好,内存占用更低。
内容的提问来源于stack exchange,提问作者B Porto
相关产品推荐
相关产品推荐

