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"); } }
问题原因
- UI冻结:原代码里的加密逻辑在
onDone回调中执行,属于主线程任务。视频文件字节量较大时,加密操作会占用主线程资源,导致UI无法响应。 - 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
相关产品推荐
相关产品推荐

