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

Flutter视频加密致应用卡顿,使用isolate遇参数不可发送异常求助

解决Flutter视频加密UI冻结与Isolate传参异常问题

问题情况

开发Flutter视频应用,支持用户观看、下载视频。为防止已下载视频被分享,需要对下载后的视频加密存储,但加密过程会让应用UI冻结约10秒。尝试过多种加密方法都没解决卡顿问题,听说用compute方法能搞定,但一开始没实现成功;后来试Isolate功能时,又碰到「Illegal argument in isolate message: object is unsendable」异常,最终通过把加密逻辑拆成独立静态函数解决了所有问题。

原问题代码

Future<void> downloadVideo(String url) async {
  var appDocDir = await getExternalStorageDirectory();
  String appDocPath = appDocDir!.path;
  String filePath = '$appDocPath/Videos/${url.split('/').last}';

  try {
    var request = await HttpClient().getUrl(Uri.parse(url));
    var response = await request.close();
    List<int> bytes = [];
    int downloaded = 0;
    int total = response.contentLength!;
    response.listen(
      (List<int> newBytes) {
        bytes.addAll(newBytes);
        downloaded += newBytes.length;
        downloadProgress.value = downloaded / total;
        print("Download progress: ${downloadProgress.value}");
      },
      onDone: () async {
        // 生成加密密钥和IV
        final key = encrypt.Key.fromSecureRandom(32);
        final iv = encrypt.IV.fromSecureRandom(16);

        // 加密视频字节数据
        final encrypter =
            encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
        final encryptedBytes = encrypter.encryptBytes(bytes, iv: iv).bytes;

        // 安全存储密钥和IV
        final storage = FlutterSecureStorage();
        await storage.write(key: 'key_$filePath', value: key.base64);
        await storage.write(key: 'iv_$filePath', value: iv.base64);

        // 保存加密后的视频
        File file = File(filePath);
        await file.writeAsBytes(encryptedBytes);
        print("Download completed: $filePath");
      },
      onError: (e) {
        print("Error downloading video: $e");
      },
      cancelOnError: true,
    );
  } catch (e) {
    print("Error downloading video: $e");
  }
}

问题原因

  1. UI冻结:原代码里的加密逻辑在onDone回调中执行,属于主线程任务。视频文件字节量较大时,加密操作会占用主线程资源,导致UI无法响应。
  2. Isolate传参异常:直接在Isolate中使用Key、IV这类对象时,因为它们不属于可跨Isolate传递的序列化类型,所以触发了参数非法的异常。

解决代码

第一步:抽离静态加密函数

把加密逻辑单独写成静态函数,确保参数和返回值都是可序列化的基础类型:

// 独立静态加密函数,仅处理纯数据转换,避免传递不可序列化对象
static Map<String, dynamic> encryptVideoBytes(List<int> rawBytes) {
  final key = encrypt.Key.fromSecureRandom(32);
  final iv = encrypt.IV.fromSecureRandom(16);
  final encrypter = encrypt.Encrypter(encrypt.AES(key, mode: encrypt.AESMode.cbc));
  final encryptedBytes = encrypter.encryptBytes(rawBytes, iv: iv).bytes;

  return {
    'encryptedBytes': encryptedBytes,
    'keyBase64': key.base64,
    'ivBase64': iv.base64,
  };
}

第二步:修改下载函数使用compute调用

在onDone回调中用compute执行加密逻辑,把加密任务移到后台Isolate:

Future<void> downloadVideo(String url) async {
  var appDocDir = await getExternalStorageDirectory();
  String appDocPath = appDocDir!.path;
  String filePath = '$appDocPath/Videos/${url.split('/').last}';

  try {
    var request = await HttpClient().getUrl(Uri.parse(url));
    var response = await request.close();
    List<int> bytes = [];
    int downloaded = 0;
    int total = response.contentLength!;
    response.listen(
      (List<int> newBytes) {
        bytes.addAll(newBytes);
        downloaded += newBytes.length;
        downloadProgress.value = downloaded / total;
        print("Download progress: ${downloadProgress.value}");
      },
      onDone: () async {
        // 用compute在后台Isolate执行加密,避免阻塞主线程
        final encryptionResult = await compute(encryptVideoBytes, bytes);

        final encryptedBytes = encryptionResult['encryptedBytes'] as List<int>;
        final keyBase64 = encryptionResult['keyBase64'] as String;
        final ivBase64 = encryptionResult['ivBase64'] as String;

        // 安全存储密钥和IV
        final storage = FlutterSecureStorage();
        await storage.write(key: 'key_$filePath', value: keyBase64);
        await storage.write(key: 'iv_$filePath', value: ivBase64);

        // 保存加密后的视频
        File file = File(filePath);
        await file.writeAsBytes(encryptedBytes);
        print("Download completed: $filePath");
      },
      onError: (e) {
        print("Error downloading video: $e");
      },
      cancelOnError: true,
    );
  } catch (e) {
    print("Error downloading video: $e");
  }
}

核心说明

  • compute是Flutter封装好的Isolate工具,自动创建、管理后台Isolate,不用手动处理Isolate的通信和销毁。
  • 静态函数里只处理纯数据转换,把Key、IV转成base64字符串返回,避免跨Isolate传递不可序列化对象。
  • 加密任务移到后台后,主线程只处理UI和轻量操作,彻底解决了UI冻结问题。

内容的提问来源于stack exchange,提问作者Alex Mact

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:13:20