写点什么

Flutter 中的异步和多进程

作者:凌宇之蓝
  • 2024-06-19
    北京
  • 本文字数:3843 字

    阅读完需:约 13 分钟

Flutter中的异步和多进程

Flutter 是一个用于创建高性能、高保真度移动应用的框架,它使用 Dart 编程语言。


在 Flutter 中,异步和多进程是两种不同的概念,用于解决不同的问题。

异步 (Asynchronous)

异步编程是一种编程范式,允许代码在等待操作完成(如网络请求、文件 I/O)时继续执行其他任务,而不是阻塞主线程。Dart 是单线程执行的,但它提供了异步编程的方式,主要通过 FutureStream API,以及 asyncawait 关键字。


  • Future: 代表一个将来可能完成的计算结果。你可以用 then 方法来注册回调,当 Future 完成时调用。

  • Stream: 提供了一种方式来获取一系列异步数据事件。

  • async 和 await: 允许你写出看起来像同步代码的异步代码。异步编程在 Flutter 中非常重要,因为它确保了 UI 的流畅性,避免了因为长时间运行的任务而导致的界面卡顿。

多进程 (Multi-process)

多进程是指一个程序同时运行多个进程。在 Flutter 中,由于它通常是用来构建移动应用的,多进程不是常见的做法,因为移动操作系统通常为每个应用分配一个进程。然而,在特殊情况下,例如需要处理大量数据或者需要与操作系统深度集成时,可能会考虑使用多进程。在 Flutter 中实现多进程可能涉及到以下内容:


  • Isolates: Dart 的执行模型是事件循环和隔离,隔离是 Dart 中的并发单元。每个隔离有自己的内存堆和事件循环,允许同时运行多个任务,而不会影响主线程的性能。Isolates 之间的通信通过消息传递进行。

  • Platform channels: Flutter 与原生代码通信的方式,可以实现在 Flutter 应用和原生应用之间传递数据。

如何实现异步

在 Flutter 中,实现异步的常用方法有以下几种:


  1. 使用Future


Future是 Dart 中表示未来可能完成的计算或 I/O 操作的结果的对象。你可以使用Future直接进行异步操作,例如:


void main() {  fetchData().then((data) {    print('Data fetched: $data');  });}
Future<String> fetchData() async { // 模拟网络请求或其他耗时操作 await Future.delayed(Duration(seconds: 2)); return 'Hello, World!';}
复制代码


  1. 使用async/await


asyncawait关键字可以让你以同步的方式编写异步代码,提高代码的可读性。要使用async/await,首先确保你的函数被声明为async,然后在该函数内部使用await关键字等待Future完成:


void main() async {  String data = await fetchData();  print('Data fetched: $data');}
Future<String> fetchData() async { // 模拟网络请求或其他耗时操作 await Future.delayed(Duration(seconds: 2)); return 'Hello, World!';}
复制代码


  1. 使用Stream


Stream是 Dart 中用于处理异步事件流的对象。你可以使用Stream来处理多个异步操作,例如:


void main() {  StreamSubscription subscription = fetchData().listen((data) {    print('Data fetched: $data');  });}
Stream<String> fetchData() async* { for (int i = 0; i < 3; i++) { // 模拟网络请求或其他耗时操作 await Future.delayed(Duration(seconds: 2)); yield 'Hello, World! $i'; }}
复制代码


这些方法可以帮助你在 Flutter 应用中实现异步操作,从而避免阻塞主线程,提高应用的响应性能。在实际开发中,可以根据具体需求选择合适的方法。

如何实现多进程

在 Flutter 中,实现多进程主要依赖于 Dart 语言的Isolate


  1. 首先,创建一个新的Isolate,这里我们将其命名为background_isolate。为此,我们需要定义一个IsolateNameServer实例,并为其分配一个唯一的名称:


import 'dart:isolate';
void main() async { final isolateNameServer = IsolateNameServer(); final name = 'background_isolate'; final uri = Uri.parse('isolate://$name');}
复制代码


  1. 接下来,创建一个名为backgroundTask的函数,该函数将在新的Isolate中运行:


void backgroundTask(SendPort sendPort) {  // 在这里执行后台任务  sendPort.send('后台任务完成');}
复制代码


  1. 现在,我们需要启动新的Isolate,并将backgroundTask函数作为入口点传递给它。同时,我们需要为新Isolate设置一个ReceivePort,以便接收来自主线程的消息:


void main() async {  // ...前面的代码
final receivePort = ReceivePort(); isolateNameServer.registerUri(name, uri);
await Isolate.spawnUri(uri, [], backgroundTask, onExit: receivePort.sendPort);
// 监听来自后台Isolate的消息 receivePort.listen((message) { print('收到消息:$message'); });}
复制代码


  1. 若要从主线程向Isolate发送消息,可以使用以下代码:


void main() async {  // ...前面的代码
// 向后台Isolate发送消息 final sendPort = await IsolateNameServer.lookupUri(uri).then((port) => port.sendPort); sendPort.send('开始执行后台任务');}
复制代码


  1. 最后,为了在 Flutter 应用中使用这个多进程功能,可以将上述代码封装到一个方法中,并在需要的地方调用它。例如,在一个按钮点击事件中启动多进程任务:


import 'package:flutter/material.dart';
void main() { runApp(MyApp());}
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Flutter Multi-process Example')), body: Center( child: ElevatedButton( onPressed: () { startBackgroundTask(); }, child: Text('Start Background Task'), ), ), ), ); }
void startBackgroundTask() { // 将前面提到的代码封装到这里 }}
复制代码


这样,当用户点击按钮时,应用程序将启动一个新的Isolate来执行后台任务,从而实现多进程功能。注意,Isolate之间的通信是通过SendPortReceivePort进行的,因此它们可以相互发送和接收消息。

Isolate 之间如何处理共享资源

在使用Isolate时,由于每个Isolate都有自己的内存空间,不能直接共享资源


  1. 使用SendPortReceivePort进行通信:通过Isolate之间的消息传递,可以让一个Isolate访问另一个Isolate中的资源。例如,一个Isolate负责读取数据库,另一个Isolate负责处理数据。当一个Isolate需要访问数据库时,它可以向另一个Isolate发送请求,然后等待响应。这种方法可以实现资源共享,但可能会导致较高的延迟,因为需要在Isolate之间传输数据。


示例:


// 在主Isolate中void main() async {  final receivePort = ReceivePort();  final sendPort = await Isolate.spawn(anotherIsolate, receivePort.sendPort);
// 请求数据 sendPort.send('获取数据');
// 监听响应 receivePort.listen((data) { print('收到数据:$data'); });}
// 在另一个Isolate中void anotherIsolate(SendPort sendPort) async { final receivePort = ReceivePort(); sendPort.send(receivePort.sendPort);
await for (final message in receivePort) { if (message == '获取数据') { // 从数据库获取数据 final data = await getDataFromDatabase();
// 发送响应 sendPort.send(data); } }}
复制代码


  1. 使用IsolateNameServer注册服务:通过IsolateNameServer,可以在多个Isolate之间注册和查找服务。这种方法允许一个Isolate充当服务器的角色,而其他Isolate可以通过名称查找并连接到该服务器。这种方式可以实现资源共享,但需要更多的设置和管理。


示例:


// 在主Isolate中void main() async {  final isolateNameServer = IsolateNameServer();  final name = 'database_service';  final uri = Uri.parse('isolate://$name');
final sendPort = await IsolateNameServer.lookupUri(uri).then((port) => port.sendPort);
// 请求数据 sendPort.send('获取数据');
// 监听响应 final receivePort = ReceivePort(); sendPort.send(receivePort.sendPort); receivePort.listen((data) { print('收到数据:$data'); });}
// 在数据库服务Isolate中void databaseService(SendPort sendPort) async { final receivePort = ReceivePort(); final isolateNameServer = IsolateNameServer(); final name = 'database_service'; isolateNameServer.registerUri(name, Uri.parse('isolate://$name'));
sendPort.send(receivePort.sendPort);
await for (final message in receivePort) { if (message == '获取数据') { // 从数据库获取数据 final data = await getDataFromDatabase();
// 发送响应 sendPort.send(data); } }}
复制代码


  1. 使用状态管理库:对于更复杂的状态管理需求,可以考虑使用诸如RiverpodBloc等状态管理库。这些库提供了一种集中管理应用状态的方法,并允许在不同的Isolate之间共享状态。虽然这些库主要用于管理应用状态,但它们也可以用于处理资源共享问题。


总之,虽然Isolate之间不能直接共享资源,但通过消息传递和服务注册等方法,可以实现资源共享。在实际应用中,可以根据具体需求和场景选择合适的方法。

总结

简而言之,异步编程关注的是单个线程内如何处理可能阻塞的操作,而多进程则涉及到如何利用多个 CPU 核心和内存空间来并发执行任务。在 Flutter 中,由于它是单线程的,并且 UI 操作是线程绑定的,异步编程是确保应用响应性和流畅性的关键。而多进程在 Flutter 中使用较少,通常用于处理计算密集型任务或者与原生代码的交互。

发布于: 刚刚阅读数: 3
用户头像

凌宇之蓝

关注

代码改变世界 2018-04-24 加入

【君伟说】公众号作者,移动技术开发工作者。

评论

发布
暂无评论
Flutter中的异步和多进程_凌宇之蓝_InfoQ写作社区