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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:47:35