当前位置: 首页 > web >正文

Flutter - UIKit开发相关指南 - 线程和异步

线程和异步

编写异步代码

Dart采用单线程执行模型,支持Isolates(在另一个线程上运行Dart代码)、事件循环和异步编程。除非生成一个Isolates,否则Dart代码将在主UI线程中运行,并由事件循环驱动。Flutter的事件循环相当于iOS的主线程上的RunLoop。

Dart的单线程模型,不代表阻塞型的操作都会导致UI卡顿。实际上可以采用Dart语言提供的异步功能比如async/await来执行异步的操作。

因为要请求网络,所以添加http模块

$ fltter pub add http
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';void main() {runApp(const MainApp());
}class MainApp extends StatelessWidget {const MainApp({super.key});Widget build(BuildContext context) {return const MaterialApp(home: ThreadSample());}
}class ThreadSample extends StatefulWidget {const ThreadSample({super.key});State<ThreadSample> createState() => _ThreadSampleState();
}class _ThreadSampleState extends State<ThreadSample> {List<Map<String, Object?>> data = [];/// 1. 初始化_ThreadSampleState Widget的状态void initState() {super.initState();/// 2.加载数据loadData();}Future<void> loadData() async {/// 3. 发起异步请求final Uri dataURL = Uri.parse('https://jsonplaceholder.typicode.com/posts');final http.Response response = await http.get(dataURL);/// 4. 等响应结束后调用setState() 更新data 触发build方法setState(() {data = (jsonDecode(response.body) as List).cast<Map<String, Object?>>();});}Widget getRow(int index) {return Padding(padding: const EdgeInsets.all(10),child: Text('Row ${data[index]['title']}'),);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('线程与异步示例')),// 5. 显示列表,长度为data.length,内容通过getRow方法返回data的子元素body: ListView.builder(itemCount: data.length,itemBuilder: (context, index) {return getRow(index);},),);}
}

2025-05-12 16.39.30.png

切到后台线程

因为Flutter是单线程模型,不需要考虑线程管理相关的问题。在执行I/O密集型的操作时,比如访问磁盘或网络,可以使用async/await,但是当在执行CPU计算密集型的操作时,则应该将其移到独立线程(Isolate)以避免阻塞事件循环。

Isolates 是独立的执行线程,它们与主线程内存堆不共享任何内存。这意味着你无法访问主线程中的变量,或通过调用 setState() 来更新用户界面。

import 'dart:async';
import 'dart:convert';
import 'dart:isolate';import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;void main() {runApp(const SampleApp());
}class SampleApp extends StatelessWidget {const SampleApp({super.key});Widget build(BuildContext context) {return const MaterialApp(title: 'Sample App', home: SampleAppPage());}
}class SampleAppPage extends StatefulWidget {const SampleAppPage({super.key});State<SampleAppPage> createState() => _SampleAppPageState();
}class _SampleAppPageState extends State<SampleAppPage> {List<Map<String, Object?>> data = [];void initState() {super.initState();/// 主1. 加载数据loadData();}bool get showLoadingDialog => data.isEmpty;Future<void> loadData() async {/// Opens a long-lived port for receiving messages./// 打开端口用于接收数据final ReceivePort receivePort = ReceivePort();/// 主2.Isolate开启子线程/// The [entryPoint] function must be able to be called with a single/// argument, that is, a function which accepts at least one positional/// parameter and has at most one required positional parameter.////// The entry-point function is invoked in the new isolate with [message]/// as the only argument./// 第一个参数:至少包含一个参数的函数指针,这里关联的是dataLoader,参数是SendPort////// [message] must be sendable between isolates. Objects that cannot be sent/// include open files and sockets (see [SendPort.send] for details). Usually/// the initial [message] contains a [SendPort] so that the spawner and/// spawnee can communicate with each other./// 第二个参数: 不同Isolate之间传递的数据,通常初始化时传的message包含一个SendPort////// receivePort.sendPort/// [SendPort]s are created from [ReceivePort]s./// Any message sent through a [SendPort] is delivered to its corresponding [ReceivePort]./// There might be many [SendPort]s for the same [ReceivePort]./// 通过SendPort发送的消息会传送给关联的ReceivePortawait Isolate.spawn(dataLoader, receivePort.sendPort);/// 主3. first是一个Future,它会在接收到第一个消息时完成/// 一旦收到第一个消息,它就会关闭ReceivePort,并且不再监听其它消息/// 适用于只接收单个消息的情况final SendPort sendPort = await receivePort.first as SendPort;try {/// 主4. 使用await调用sendReceivefinal List<Map<String, dynamic>> msg = await sendReceive(sendPort,'https://jsonplaceholder.typicode.com/posts',);/// 主5.设置数据,通知Flutter刷新UIsetState(() {data = msg;});} catch (e) {print('Error in loadData:$e');}}// 子1. 执行子线程上的函数static Future<void> dataLoader(SendPort sendPort) async {// 子2.打开端口接收数据final ReceivePort port = ReceivePort();/// 子3. 发送自己的接收端口sendPort.send(port.sendPort);/// 子4:等待消息await for (final dynamic msg in port) {/// 子5: 接收到url + 主线程的接收端口final String url = msg[0] as String;final SendPort replyTo = msg[1] as SendPort;/// 子6: 发起网络请求final Uri dataURL = Uri.parse(url);final http.Response response = await http.get(dataURL);/// 下面这种写法在sendReceive会报/// Unhandled/// Exception: type 'Future<dynamic>' is not a subtype of type/// 'Future<List<Map<String, dynamic>>>'////// replyTo.send(jsonDecode(response.body) as List<Map<String, dynamic>>);/// 因为Dart在运行时无法检查Future<T>中的T,直接转换Future的泛型参数会失败/// 强制类型转换final data = jsonDecode(response.body) as List;final typedata = data.cast<Map<String, dynamic>>();/// 子7: 将网络请求的结果发送到主线程replyTo.send(typedata);}}Future<dynamic> sendReceive(SendPort port, String msg) {// 主5.创建接收数据的端口final ReceivePort response = ReceivePort();// Sends an asynchronous [message] through this send port, to its corresponding [ReceivePort].// 主6. 主线程异步发送url + 通知其它线程接收端口port.send(<dynamic>[msg, response.sendPort]);return response.first;}Widget getBody() {/// 数据为空显示进度条bool showLoadingDialog = data.isEmpty;if (showLoadingDialog) {return getProgressDialog();} else {return getListView();}}Widget getProgressDialog() {return const Center(child: CircularProgressIndicator());}ListView getListView() {return ListView.builder(itemCount: data.length,itemBuilder: (context, position) {return getRow(position);},);}Widget getRow(int i) {return Padding(padding: const EdgeInsets.all(10),child: Text("Row ${data[i]["title"]}"),);}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Sample App')),body: getBody(),);}
}

202505131636-w400

错误

[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: ClientException with SocketException: Failed host lookup: 'jsonplaceholder.typicode.com'

[ERROR:flutter/runtime/dart_vm_initializer.cc(40)] Unhandled Exception: ClientException with SocketException: Failed host lookup: ‘jsonplaceholder.typicode.com’ (OS Error: nodename nor servname provided, or not known, errno = 8), uri=https://jsonplaceholder.typicode.com/posts

首次启动需要同意网络权限,看报错是DNS找不到域名,所以还是网络问题,在手机上授权后再重新用flutter运行工程能恢复

参考

1.给 UIKit 开发者的 Flutter 指南
2.flutter 中 ReceivePort 的 first 和 listen

http://www.xdnf.cn/news/6248.html

相关文章:

  • Seed1.5-VL:高效通用的视觉-语言基础模型
  • leetcode - 滑动窗口问题集
  • Qt 自定义槽 + 自定义信号(9)
  • 《数据库原理》部分习题解析1
  • 使用Haproxy搭建高效Web群集的完整指南
  • MATLAB中heatmap函数
  • 基于开源链动2+1模式AI智能名片S2B2C商城小程序的低集中度市场运营策略研究
  • RHCE认证通过率
  • LeetCode Hot100 (1/100)
  • 家庭宽带的内网穿透实践
  • 数学实验(Matlab符号运算)
  • 面试篇: Redis(持续更新)
  • vue3基础学习 [简单标签] (vscode)
  • More Effective C++:改善编程与设计(上)
  • Redis内存淘汰策略和过期键删除策略有哪些?
  • Flutter在键盘的上方加一个完成按钮
  • JAVA异常体系
  • Linux proc文件系统 内存影射
  • YOLO11解决方案之热力图探索
  • 二分查找的边界问题
  • KUKA机器人中断编程3—暂停功能的编程
  • Selenium-Java版(环境安装)
  • fio 命令在 Linux 系统中的应用示例
  • Android锁
  • android studio导入项目
  • json-server的用法-基于 RESTful API 的本地 mock 服务
  • jQuery知识框架
  • Spring Cloud Gateway 聚合 Swagger 文档:一站式API管理解决方案
  • 鸿蒙OSUniApp 实现精美的用户登录和注册页面#三方框架 #Uniapp
  • Vue ElementUI原生upload修改字体大小和区域宽度