在前面的篇章中, 我们已经知道了, 在 Dart 中,Isolate 是一种轻量级的线程,可以并发执行代码。并且它的主要应用场景之一是在后台 Isolate 中执行耗时的计算任务,不会阻塞应用程序的主线程。Dart 代码运行在一个独立的root isolate中,而 isolate 之间不共享内存, 多个isolate之间通过消息传递数据。
在 Flutter 3.7之前, 我们在root isolate中用shared_preferences缓存一个简单的String类型的变量。之后在子isolate里, 我们是获取不到刚缓存的String类型的值。这是因为, 切换了 isolate ,它就会变为 null。isolate之间不共享内存。现在有了后台isolate, 我们就可以获取到了。后台任务依赖于root isolate提供token。
/// A token that represents a root isolate. class RootIsolateToken { RootIsolateToken._(this._token); /// An enumeration representing the root isolate (0 if not a root isolate). final int _token; /// The token for the root isolate that is executing this Dart code. If this /// Dart code is not executing on a root isolate [instance] will be null. static final RootIsolateToken? instance = () { final int token = __getRootIsolateToken(); return token == 0 ? null : RootIsolateToken._(token); }(); @Native<Int64 Function()>(symbol: 'PlatformConfigurationNativeApi::GetRootIsolateToken') external static int __getRootIsolateToken(); }表示root isolate的令牌token。
/// A [BinaryMessenger] for use on background (non-root) isolates. class BackgroundIsolateBinaryMessenger extends BinaryMessenger { BackgroundIsolateBinaryMessenger._(); final ReceivePort _receivePort = ReceivePort(); final Map<int, Completer<ByteData?>> _completers = <int, Completer<ByteData?>>{}; int _messageCount = 0; }_receivePort: 一个 ReceivePort 实例,用于接收从其他隔离体发送过来的消息。
_completers:: 一个 Map,用于存储消息的 Completer 对象。Completer 用于异步地等待消息的返回结果。在这个 Map 中,消息的标识符(通常是一个唯一的整数)与对应的 Completer 关联。
static void ensureInitialized(ui.RootIsolateToken token) { if (_instance == null) { ui.PlatformDispatcher.instance.registerBackgroundIsolate(token); final BackgroundIsolateBinaryMessenger portBinaryMessenger = BackgroundIsolateBinaryMessenger._(); _instance = portBinaryMessenger; portBinaryMessenger._receivePort.listen((dynamic message) { try { final List<dynamic> args = message as List<dynamic>; final int identifier = args[0] as int; final Uint8List bytes = args[1] as Uint8List; final ByteData byteData = ByteData.sublistView(bytes); portBinaryMessenger._completers .remove(identifier)! .complete(byteData); } catch (exception, stack) { FlutterError.reportError(FlutterErrorDetails( exception: exception, stack: stack, library: 'services library', context: ErrorDescription('during a platform message response callback'), )); } }); } }实现后台隔离的核心代码。
将上面获取的root isolate的令牌token传进来,跟BackgroundIsolate关联起来,也就是注册后台隔离体。这样,BackgroundIsolate就跟root isolate建立了通信连接,BackgroundIsolate和root isolate之间就可以传递消息了。
floatingActionButton: FloatingActionButton( onPressed: () { final RootIsolateToken _rootIsolateToken = RootIsolateToken.instance!; Isolate.spawn(_isolateMain, _rootIsolateToken); }, tooltip: 'Tap', child: const Icon(Icons.add), ),点击按钮, 获取之前用shared_preferences缓存的String类型的值。
/// 顶层函数 _isolateMain(RootIsolateToken rootIsolateToken) async { /// Register the background isolate with the root isolate. BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken); /// 你现在可以用shared_preferences插件了。 final SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); final bool? _isDebug = sharedPreferences.getBool('isDebug'); print('---😆😆mark1, _isDebug:$_isDebug😆😆---'); /// 继续设置为false sharedPreferences.setBool('isDebug', false); }
_isolateMain任务是个顶层函数, 内部可以获取到缓存的变量值了。并且, 在子isolate里面更新缓存的变量值后, 在root isolate也可以同步更新。
FloatingActionButton( onPressed: () async { final ByteData data = await rootBundle.load('assets/sample.png'); final List<int> bytes = data.buffer.asUint8List(); img.Image image = img.decodeImage(Uint8List.fromList(bytes))!; /// token final RootIsolateToken _rootIsolateToken = RootIsolateToken.instance!; // 新建ReceivePort接收消息 final receivePort = ReceivePort(); await Isolate.spawn( _processImage, {'image': image, 'sendPort': receivePort.sendPort, 'rootIsolateToken': _rootIsolateToken}, ); // 关闭receivePort final processedImage = await receivePort.first; receivePort.close(); /// 获取存储路径 _saveImg(processedImage); }, tooltip: '编辑图片', child: const Text('编辑图片'), )旋转、保存图片
/// 顶层函数 _processImage(Map<String, dynamic> data) async { final img.Image image = data['image']; final SendPort sendPort = data['sendPort']; final RootIsolateToken rootIsolateToken = data['rootIsolateToken']; /// 注册 root isolaote BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken); // 堆代码 duidaima.com // 旋转图片90度 final img.Image rotatedImage = img.copyRotate(image, 90); // 1、发送旋转后的图片到main isolate sendPort.send(rotatedImage); // 2、或者直接保存到本地 /// _saveImg(rotatedImage); } /// 保存图片 _saveImg(img.Image rotatedImage) async { /// 获取存储路径 final Directory _directory = (await getTemporaryDirectory()); // 保存编辑后的图片到本地 final File outputFile = File(_directory.path + '/path_to_output_image.png'); await outputFile.writeAsBytes(img.encodeJpg(rotatedImage)); print('Image processing complete. Saved to ${outputFile.path}'); }