简介
在前面的篇章中, 我们已经知道了, 在 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。
RootIsolateToken
/// 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。
BackgroundIsolateBinaryMessenger
信使的作用, 用于后台(非根)隔离。用消息传递实现。
/// 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 关联。
ensureInitialized
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 {
/// 注册background isolate, 使用 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也可以同步更新。
图像处理
图像处理通常需要大量的计算,例如缩放、滤镜应用或复杂的图像编辑。将图像处理操作放在后台隔离中可以减轻主线程的负担。
下面一个示例, 将图像旋转 90 度, 然后保存到文件系统中。
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);
// 旋转图片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}');
}
参考资料
BackgroundIsolateBinaryMessenger class