- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我正在试用 Flutter,但我无法让 UI 持续更新。我想在调用长时间运行的异步方法时显示一条状态消息,但我在调用长时间运行的方法之前进行的 setState() 调用似乎不会导致调用我的 build() 方法。
我创建了一个简单示例,计算 25 到 30 之间随机选择的数字的斐波那契数。在我的示例代码/应用程序中,点击“calc”按钮调用 _calc()。 _calc() 选择一个随机数,设置一个状态消息“正在计算 $num 的 Fib...” 绑定(bind)到文本小部件 (_status) 并使用 setState() 更新它;然后调用 async _fib() 例程来计算数字;然后使用 setState() 用结果更新 _status。此外,build() 方法将 _status 的值打印到控制台,可用于查看何时调用 build()。
实际上,按下按钮时,第一条状态消息既不会出现在调试控制台中,也不会出现在 UI 上。做了一些实验,我添加了一个伪 sleep 函数,我在调用 _fib() 之前调用它。这有时会导致第一个 setState() 调用正常工作 - 调用 build()。我睡得越久,它就越有效。 (我使用的值从几毫秒到一整秒)。
所以我的问题是:我做错了什么?什么是正确的方法来做到这一点?使用伪 sleep 显然不是正确的解决方案。
其他,可能不太相关的信息:我的开发环境是 Win10 机器上的 Android Studio 3.1.2。使用 Android SDK 27.0.3,Flutter beta 0.3.2。我的目标设备是运行 Android 8.1 的 pixel2 模拟器。另外,抱歉,如果我缺少"new"关键字令人反感,但从我在 Dart 2 发行说明中读到的内容来看,现在通常不需要这样做。
import 'package:flutter/material.dart';
import "dart:async";
import "dart:math";
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Debug Toy',
home: MyWidget(),
);
}
}
class MyWidget extends StatefulWidget {
@override
MyWidgetState createState() => MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
String _status = "Initialized";
final rand = Random();
Future sleep1() async {
return new Future.delayed(const Duration(milliseconds: 100),() => "1");
}
Future<Null> _resetState() async {
setState(() { _status = "State Reset"; });
}
Future<Null> _calc() async {
// calculate something that takes a while
int num = 25 + rand.nextInt(5);
setState(() { _status = "Calculating Fib of $num..."; });
//await sleep1(); // without this, the status above does not appear
int fn = await _fib(num);
// update the display
setState(() { _status = "Fib($num) = $fn"; });
}
Future<int> _fib(int n) async {
if (n<=0) return 0;
if ((n==1) || (n==2)) return 1;
return await _fib(n-1) + await _fib(n-2);
}
@override
Widget build(BuildContext context) {
print("Build called with status: $_status");
return Scaffold(
appBar: AppBar(title: Text('Flutter Debug Toy')),
body: Column(
children: <Widget>[
Container(
child: Row(children: <Widget>[
RaisedButton( child: Text("Reset"), onPressed: _resetState, ),
RaisedButton( child: Text("Calc"), onPressed: _calc, )
]),
),
Text(_status),
],
),
);
}
}
最佳答案
让我们从一个极端开始,将 fib
重写为 fibSync
int fibSync(int n) {
if (n <= 0) return 0;
if (n == 1 || n == 2) return 1;
return fibSync(n - 1) + fibSync(n - 2);
}
然后调用它
Future<Null> _calc() async {
// calculate something that takes a while
int num = 25 + rand.nextInt(5);
setState(() {
_status = "Calculating Fib of $num...";
});
//await Future.delayed(Duration(milliseconds: 100));
int fn = fibSync(num);
// update the display
setState(() {
_status = "Fib($num) = $fn";
});
}
第一个 setState 只是将 Widget 标记为需要重建并且(没有'sleep')继续直接进入计算,从不给框架重建 Widget 的机会,因此不会显示'Calculating'消息.第二个 setState 在计算之后调用,并再次(冗余地)将 Widget 标记为需要重建。
所以,执行顺序是:
当我们取消对'sleep'的注释时,执行顺序变为
(顺便说一句,请注意同步 fib 计算是如何快一个数量级的,因为它不必执行所有微任务调度。)
让我们重新考虑异步计算。让它异步的动机是什么?以便 UI 在计算期间保持响应?如您所见,这并没有达到预期的效果。您仍然只有一个执行线程,并且不允许在执行过程中出现任何间隙以发生回调和渲染。休眠 100 毫秒不受计算限制,因此可能会发生绘图等。
我们使用异步函数来等待外部事件,比如来自网络服务器的回复,在回复到达之前我们无事可做,我们可以利用这段时间继续渲染显示、对手势使用react等.
对于计算绑定(bind)的东西,你需要一个 second thread of execution这是通过 Isolate
实现的。一个 isolate 有它自己的堆,所以你必须把它的数据传递给它,它在它自己的空间里工作,然后传回一些结果。如果花费的时间太长,或者用户取消等,您也可以停止它。
(计算 fibs 的计算成本要低得多,但我想我们正在使用递归版本作为 O(n^2) 函数的一个很好的例子,对吧?)
关于Flutter setState() 并不总是调用我的构建方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50399346/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!