Flutter 线程模型详解:主线程、异步与 Isolate


一、主线程:默认的执行环境

所有代码默认运行在主线程。下面的例子展示了一个会阻塞主线程的错误示范:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  String _result = '点击按钮开始计算';
  bool _isLoading = false;

  // 一个模拟的、非常耗时的同步计算函数
  // 这会严重阻塞主线程!
  int _expensiveTask(int number) {
    int sum = 0;
    for (int i = 0; i < number; i++) {
      sum += i;
    }
    return sum;
  }

  void _doTaskOnMainThread() {
    setState(() {
      _isLoading = true;
    });

    // 这个计算直接在UI线程上运行,会导致界面卡死
    int result = _expensiveTask(10000000000); // 一个很大的数

    setState(() {
      _result = '计算结果: $result';
      _isLoading = false;
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('线程模型实战')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_isLoading) const CircularProgressIndicator(),
            Text(_result, style: Theme.of(context).textTheme.headlineSmall),
            ElevatedButton(
              onPressed: _doTaskOnMainThread,
              // 点击这个按钮后,UI会完全卡住,连Loading圈都无法转动
              child: const Text('在主线程执行耗时任务 (错误示范)'),
            ),
          ],
        ),
      ),
    );
  }
}

现象:点击按钮后,即使看到了 Loading 圆圈,它也会完全冻结,无法动画,直到计算完成。这就是阻塞主线程的后果。


二、异步操作:处理 I/O 任务(网络请求为例)

使用 async/await 和 Future 处理不会阻塞线程的“等待型”任务。

// ... 承接上面的 State class ...

// 在 _HomePageState 类中添加新方法
Future<void> _fetchDataAsync() async {
  setState(() {
    _isLoading = true;
    _result = '正在加载...';
  });

  // 使用 async/await 进行异步网络请求
  // http.get 是非阻塞的,它向系统发出请求后就直接返回了
  try {
    // 记得添加 http 包: flutter pub add http
    // import 'package:http/http.dart' as http;
    final response = await http.get(Uri.parse('https://api.example.com/data'));
    
    // 这里的代码会在网络请求成功后,由事件循环调度执行
    // 它仍然运行在主线程,但等待期间主线程是空闲的,可以处理其他事件(如动画)
    if (response.statusCode == 200) {
      setState(() {
        _result = '获取成功: ${response.body.substring(0, 100)}...';
      });
    } else {
      setState(() {
        _result = '请求失败: ${response.statusCode}';
      });
    }
  } catch (e) {
    setState(() {
      _result = '发生错误: $e';
    });
  } finally {
    setState(() {
      _isLoading = false;
    });
  }
}

// 在 build 方法的 Column 中添加新的按钮
ElevatedButton(
  onPressed: _fetchDataAsync,
  child: const Text('执行异步网络请求 (正确示范)'),
),

现象:点击按钮后,Loading 圆圈流畅旋转,UI 可以正常响应。数据获取完成后,结果会更新到屏幕上。


三、Isolate:处理 CPU 密集型任务

使用 compute 函数将耗时计算任务移到后台 Isolate。

// 1. 首先,在文件顶部导入 foundation.dart
import 'package:flutter/foundation.dart';

// 2. 确保耗时函数是顶级函数或静态方法
// 因为 Isolate 不能访问原实例中的非静态成员
int _expensiveTaskInIsolate(int number) {
  int sum = 0;
  for (int i = 0; i < number; i++) {
    sum += i;
  }
  return sum;
}

// 3. 在 _HomePageState 类中添加新方法
void _doTaskInIsolate() async {
  setState(() {
    _isLoading = true;
    _result = '在Isolate中计算...';
  });

  // 使用 compute 将函数和参数抛到新的Isolate中执行
  // 这是一个非阻塞调用,会立即返回一个Future
  int result = await compute(_expensiveTaskInIsolate, 10000000000);

  // compute 完成后,这里的代码会在主线程执行,可以安全地更新UI
  setState(() {
    _result = 'Isolate计算结果: $result';
    _isLoading = false;
  });
}

// 4. 在 build 方法的 Column 中添加新的按钮
ElevatedButton(
  onPressed: _doTaskInIsolate,
  child: const Text('使用Isolate执行计算 (正确示范)'),
),

现象:点击按钮后,Loading 圆圈流畅旋转,UI 操作完全正常,毫无卡顿。计算完成后,结果会更新到屏幕上。


最终效果对比

你的 build 方法中的 Column 最终会有三个按钮:

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    if (_isLoading) const CircularProgressIndicator(),
    Text(_result, style: Theme.of(context).textTheme.headlineSmall),
    ElevatedButton(
      onPressed: _doTaskOnMainThread,
      child: const Text('在主线程执行耗时任务 (错误示范)'),
    ),
    SizedBox(height: 10),
    ElevatedButton(
      onPressed: _fetchDataAsync,
      child: const Text('执行异步网络请求 (正确示范)'),
    ),
    SizedBox(height: 10),
    ElevatedButton(
      onPressed: _doTaskInIsolate,
      child: const Text('使用Isolate执行计算 (正确示范)'),
    ),
  ],
)

总结一下:

· 错误示范按钮:会让你体验到应用卡死的糟糕感觉。
· 异步网络请求按钮:展示了如何用 async/await 正确处理 I/O 操作。
· Isolate 计算按钮:展示了如何用 compute 正确处理 CPU 密集型任务。

通过这个实战对比,你能清晰地看到三种方式的巨大差异,并理解在正确场景使用正确工具的重要性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值