Flutter Android TV图片预览崩溃:try-catch失效,FileNotFoundException错误
问题解决:Flutter Android TV图片压缩崩溃(EFAULT Bad address)
核心原因
从堆栈追踪看,崩溃是Java层抛出的Error类型异常(而非普通Exception),Dart默认的try-catch只能捕获Exception子类,无法拦截底层JNI/Java层的Error,这就是你加了try-catch仍崩溃的直接原因。另外EFAULT (Bad address)通常关联:文件本身损坏、存储IO异常、跨线程访问时路径有效性失效。
解决方案
1. 捕获所有异常/错误类型
修改_resizeImage的catch范围,捕获Dart中所有可抛出对象(包括底层Error),同时提前校验文件状态:
Future<XFile?> _resizeImage(XFile imageFile) async { try { debugPrint("imageFile.path : ${imageFile.path}"); // 提前校验文件可用性 final file = File(imageFile.path); if (!await file.exists() || !await file.canRead()) { debugPrint("文件不可访问"); return null; } // 避免强制类型转换导致额外异常 List<int>? imageBytes = await FlutterImageCompress.compressWithFile( imageFile.path, quality: 25, ); if (imageBytes == null) { debugPrint("压缩返回空数据"); return null; } // 加时间戳避免同文件名覆盖 String fName = "${DateTime.now().millisecondsSinceEpoch}_${imageFile.name}"; Directory appDocDir = await getApplicationDocumentsDirectory(); String compressedImagePath = '$appDocDir/$fName.jpg'; await File(compressedImagePath).writeAsBytes(imageBytes); return XFile(compressedImagePath); } catch (e) { // 捕获所有可抛出对象 debugPrint("图片处理失败: $e"); return null; } }
2. 替换压缩库(备选方案)
如果flutter_image_compress的底层JNI问题无法解决,改用纯Dart实现的image库,避免跨层异常逃逸:
import 'package:image/image.dart' as img; Future<XFile?> _resizeImage(XFile imageFile) async { try { final file = File(imageFile.path); if (!await file.exists() || !await file.canRead()) return null; final bytes = await file.readAsBytes(); final originalImage = img.decodeImage(bytes); if (originalImage == null) { debugPrint("无法解码图片"); return null; } // 按比例缩放(示例:宽度缩至800) final resizedImage = img.copyResize(originalImage, width: 800); final compressedBytes = img.encodeJpg(resizedImage, quality: 25); String fName = "${DateTime.now().millisecondsSinceEpoch}_${imageFile.name}"; Directory appDocDir = await getApplicationDocumentsDirectory(); String compressedImagePath = '$appDocDir/$fName.jpg'; await File(compressedImagePath).writeAsBytes(compressedBytes); return XFile(compressedImagePath); } catch (e) { debugPrint("图片处理失败: $e"); return null; } }
3. 优化StreamBuilder错误处理
在StreamBuilder中添加errorBuilder,兜底处理异常情况:
StreamBuilder<XFile?>( stream: _resize(), errorBuilder: (context, error, stackTrace) { debugPrint("流处理错误: $error"); return const Center(child: Icon(Icons.broken_image, size: 64)); }, builder: (context, snapshot) { if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) { return Image.file(File(snapshot.data!.path)); } else { return const Center(child: CircularProgressIndicator()); } }, ),
4. 适配Android TV存储特性
- 确保已申请对应权限:Android 13+申请
READ_MEDIA_IMAGES,旧版本申请READ_EXTERNAL_STORAGE,并在运行时主动请求。 - 优先通过MediaStore查询获取文件Uri,避免直接使用绝对路径导致的访问异常。
5. 隔离压缩线程(可选)
将压缩操作放入独立Isolate,避免崩溃影响主线程:
Future<XFile?> _resizeImage(XFile imageFile) async { return await Isolate.run(() async { // 这里放上述压缩逻辑代码 }); }
内容的提问来源于stack exchange,提问作者Pup In The Tree
相关产品推荐
相关产品推荐

