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.mzph.cn/news/905445.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【愚公系列】《Manus极简入门》038-数字孪生设计师:“虚实映射师”

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…

西门子WinCC Unified PC的GraphQL使用手册

TIA V20版本&#xff1a;添加用户 添加角色&#xff0c;并充分授权&#xff0c;尤其是GraphQL的读写权限。 通过SIMATIC Runtime Manager启动wincc unifi工程。 打开浏览器&#xff0c;访问本地的https://localhost/graphql/&#xff0c;运行正常如图&#xff1a; 连接外…

开源长期主义:浅谈DeepSeek技术主张与早期论文

开源、长期主义与DeepSeek的技术愿景 ©作者|格林 来源|神州问学 导入&#xff1a;Deepseek在早期就开源了许多优秀的指令模型与对话模型&#xff0c;并发布了多篇论文。以下&#xff0c;我们将基于Deepseek在早期发布的6篇论文&#xff0c;来梳理Deepseek公司的技术路径与…

TTS-Web-Vue系列:Vue3实现内嵌iframe文档显示功能

&#x1f5bc;️ 本文是TTS-Web-Vue系列的新篇章&#xff0c;重点介绍如何在Vue3项目中优雅地实现内嵌iframe功能&#xff0c;用于加载外部文档内容。通过Vue3的响应式系统和组件化设计&#xff0c;我们实现了一个功能完善、用户体验友好的文档嵌入方案&#xff0c;包括加载状态…

Elasticsearch索引设计与调优

一、分片策略设计 1.‌分片容量规划 单分片容量建议30GB(日志场景可放宽至100GB),避免超大分片引发查询延迟。分片总数计算公式:总数据量 / 30GB 1.2(20%余量应对未来增长)。主分片数创建后不可修改,副本分片数支持动态调整。2.‌分片分布优化 PUT logs-2025 { &qu…

Spring AI 集成 Mistral AI:构建高效多语言对话助手的实战指南

Spring AI 集成 Mistral AI&#xff1a;构建高效多语言对话助手的实战指南 前言 在人工智能应用开发领域&#xff0c;选择合适的大语言模型&#xff08;LLM&#xff09;与开发框架至关重要。Mistral AI 凭借其高效的多语言模型&#xff08;如 Mistral-7B、Mixtral-8x7B 等&am…

从新手到高手:全面解析 AI 时代的「魔法咒语」——Prompt

引言&#xff1a;AI 时代的「语言炼金术」 在人工智能技术突飞猛进的今天&#xff0c;我们正在经历一场堪比工业革命的生产力变革。从聊天机器人到图像生成&#xff0c;从数据分析到自动化写作&#xff0c;AI 模型正在重塑人类与信息交互的方式。而在这一切背后&#xff0c;隐…

MySQL 8.0安装(压缩包方式)

MySQL 8.0安装(压缩包方式) 下载安装包并解压 下载 https://dev.mysql.com/downloads/mysql/可关注“后端码匠”回复“MySQL8”关键字获取 解压&#xff08;我解压到D:\dev\mysql-8.4.5-winx64目录下&#xff09; 创建mysql服务 注意&#xff0c;这步之前一定要保证自己电…

免费Ollama大模型集成系统——Golang

Ollama Free V2 Web 功能实现&#xff1a;界面交互与后端逻辑 一、Web 界面概述 Ollama Free V2 的 Web 界面提供了丰富的交互功能&#xff0c;包括模型选择、图片上传、历史记录查看等。界面使用 Bootstrap 进行布局&#xff0c;结合 JavaScript 实现动态交互。 二、前端界…

【AI】人工智能数据标注细分和商业机会

一、数据标注的常见方法 数据标注是为人工智能模型训练提供高质量标签的过程&#xff0c;根据数据类型&#xff08;图像、文本、音频、视频等&#xff09;的不同&#xff0c;标注方法也有所差异&#xff1a; 1. 图像标注 分类标注&#xff1a;为图像分配类别标签&#xff08…

lanqiaoOJ 652:一步之遥 ← 扩展欧几里得定理

【题目来源】 https://www.lanqiao.cn/problems/652/learning/ 【题目背景】 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 【题目描述】 从昏迷中醒来&#xff0c;小明发现自己被关在X星球的废矿车里。矿车停在平直的废弃…

HTTP / HTTPS 协议

目录 一、前言&#xff1a; 二、Fiddler 抓包工具&#xff1a; 三、http 协议&#xff1a; 1、http 请求&#xff1a; 1.&#xff08;1&#xff09;请求行&#xff1a; 1、(2) 请求头&#xff1a; 1、(3) 请求正文: 2、http 响应&#xff1a; 2、(1) 状态码&#x…

使用泛型加载保存数据

文章速览 泛型泛型概述定义优点 实例加载数据保存数据 一个赞&#xff0c;专属于你的足迹&#xff01; 泛型 泛型概述 泛型&#xff08;Generics&#xff09;是 C# 中一种重要的编程特性&#xff0c;它允许程序员编写灵活且类型安全的代码。通过使用泛型&#xff0c;可以创建…

Redis内存淘汰策略和过期键删除策略有哪些?

Redis 提供 8 种内存淘汰策略&#xff0c;以下是详细解析及场景建议&#xff1a; 一、核心策略解析 noeviction (默认策略) 机制&#xff1a;内存满时拒绝新写入操作&#xff0c;返回错误优势&#xff1a;绝对数据安全场景&#xff1a;金融交易系统、医疗数据存储 allkeys-lr…

【C/C++】自定义类型:结构体

文章目录 前言自定义类型&#xff1a;结构体1.结构体类型的声明1.1 结构体回顾1.1.1 结构的声明 1.1.2 结构体变量的创建和初始化1.2 结构的特殊声明1.3 结构的自引用 2.结构体内存对齐2.1 对⻬规则2.2 为什么存在内存对齐&#xff1f;2.3 修改默认对⻬数 3. 结构体传参4.结构体…

PPO算法:一种先进的强化学习策略

什么是PPO算法&#xff1f; PPO&#xff08;Proximal Policy Optimization&#xff09;是一种增强学习算法&#xff0c;主要应用于解决连续控制任务。PPO算法在2017年由OpenAI提出&#xff0c;旨在解决传统策略梯度方法在连续控制任务中面临的挑战。PPO算法通过引入一个近似目…

OpenCV实现数字水印的相关函数和示例代码

OpenCV计算机视觉开发实践&#xff1a;基于Qt C - 商品搜索 - 京东 实现数字水印的相关函数 用OpenCV来实现数字水印功能&#xff0c;需要使用一些位操作函数&#xff0c;我们需要先了解一下这些函数。 1. bitwise_and函数 bitwise_and函数是OpenCV中的位运算函数之一&…

基于Python的计算机科学研究话题管理系统的设计与实现 - 爬虫

标题:基于Python的计算机科学研究话题管理系统的设计与实现 - 爬虫 内容:1.摘要 本文聚焦于基于Python的计算机科学研究话题管理系统的爬虫部分。背景是随着计算机科学研究的快速发展&#xff0c;相关话题数据海量且分散&#xff0c;人工管理效率低。目的是设计并实现一个能高…

告别手动解析!借助 CodeBuddy 快速开发网页源码提取工具

作为一名长期从事 Web 开发的程序员&#xff0c;我们在日常工作中&#xff0c;时不时会需要查看网页的源代码。这么做的目的通常是为了排查前端渲染的问题、分析接口返回的数据结构&#xff0c;或者就是单纯地想快速提取页面中的某些信息&#xff0c;比如文章链接、图片地址&am…

为什么要在 input() 后加 .strip()?

strip() 是 Python 字符串的一个方法&#xff0c;用于去除字符串开头和结尾的空白字符&#xff08;包括空格、制表符 \t、换行符 \n 等&#xff09;。 为什么要在 input() 后加 .strip()&#xff1f; 用户在输入时&#xff0c;可能会不小心在开头或结尾输入空格&#xff0c;例…