Sqflite数据存储模型&无必要使用Isolate操作数据库

Sqflite存储模型如下:

依据上图Sqflite插件的DB存储模型会有2个等待队列,一个是Flutter层同步执行队列,一个是Native层的线程执行队列

  • Android实现机制是HandlerThread,底层使用同一个静态变量,因此Query/Save读写在会同一线程队列中,不同数据库的操作也需要在同一个线程队列中排队。具体参考代码 SqflitePlugin.java

  • 其iOS的实现是通过 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),...),把任务调度到系统的后台线程排队顺序执行,原理类似于HandlerThread。只是这样会造成一个问题,如果其他功能也使用系统队列,会造成不必要的延迟。iOS定义一个数据库专用线程会更好一些。具体参考代码 SqflitePlugin.m

我们在系统层调用 Sqlite 的时候,需要注意 Sqlite 调用的耗时阻塞问题,一般都是在子线程中操作数据库,而由于 Sqflite 系统层后台线程队列的存在,在 Flutter 层,其实没必要使用 isolate 对耗时操作进行规避,这点与原生开发不同。

注意

如果确实需要在 isolate 中执行 Sqflite 相关的调用,一般建议使用 FlutterIsolate 创建 isolate ,主要原因还是 Sqlite 相关的调用都是 Platform Channel 的调用。默认情况下,在 isolate 是不允许调用这些函数的。

FlutterIsolate allows creation of an Isolate in flutter that is able to use flutter plugins. It creates the necessary platform specific bits (FlutterBackgroundView on android & FlutterEngine on iOS) to enable the platform channels to work inside an isolate.

参考链接


Dart语言构造函数中存在异步方法时的处理方法

dart语言构造函数中如果存在异步方法,编程时如果不注意特殊处理的话,很容易使代码出现未定义行为。例如下面的代码:

本意是想先输出“hello“ 然后再输出”done",然而输出顺序刚好相反。原因在于init是异步方法,本应该在调用前使用await等待异步方法返回。然而在构造函数中是无法使用await的。那么该如何处理呢?下面是一种方法,使用工厂函数产生类的对象。而工厂函数是异步的,需要配合await使用。虽然能够解决异步构造的问题,但总觉得不够优雅。

输出:

参考链接


Flutter(able) 的单例模式

一个类只允许创建一个实例,那这个类就是一个单例类,这种设计模式就叫作单例设计模式,简称单例模式。

作为最简单的一种设计模式之一,对于单例本身的概念,大家一看就能明白,但在某些情况下也很容易使用不恰当。相比其他语言,Dart 和 Flutter 中的单例模式也不尽相同,本篇文章我们就一起探究看看它在 Dart 和 Flutter 中的应用。

继续阅读Flutter(able) 的单例模式

Dart并发机制详解

Dart 通过 async-await、isolate 以及一些异步类型概念 (例如 Future 和 Stream) 支持了并发代码编程。本篇文章会对 async-await、Future 和 Stream
进行简略的介绍,而侧重点放在 isolate 的讲解上。

在应用中,所有的 Dart 代码都在 isolate 中运行。每一个 Dart 的 isolate 都有独立的运行线程,它们无法与其他 isolate 共享可变对象。在需要进行通信的场景里,isolate 会使用消息机制。尽管 Dart 的 isolate 模型设计是基于操作系统提供的进程和线程等更为底层的原语进行设计的,但在本篇文章中,我们不对其具体实现展开讨论。

大部分 Dart 应用只会使用一个 isolate (即 主 isolate),同时你也可以创建更多的 isolate,从而在多个处理器内核上达成并行执行代码的目的。

多平台使用时注意

所有的 Dart 应用都可以使用 async-await、Future 和 Stream
而 isolate 仅针对 原生平台的使用 进行实现。
使用 Dart 构建的网页应用可以 使用 Web Workers 实现相似的功能。

继续阅读Dart并发机制详解

Widget的物理模拟动画效果

物理模拟能够让应用富有真实感和更好的交互性。例如,你可能会为一个 widget 添加动画,让它看起来就像安着弹簧,或是在随重力下落。

这个指南演示了如何将 widget 从拖动的点移回到中心,并使用弹簧模拟效果。

这个演示将进行下面几步:

  1. 创建一个动画控制器
  2. 使用手势移动 widget
  3. 对 widget 进行动画
  4. 计算速度以模拟弹跳运动

继续阅读Widget的物理模拟动画效果

Flutter手势交互 ( 跟随手指运动的小球 )

Flutter 手势 - 跟随手指运动的小球

设置小球坐标变量 : 其中 currentX 是距离左侧边界的距离 , currentY 是距离右侧边界的距离 ;

小球的位置 : 小球是在 Stack 帧布局中的 Positioned 组件 , 其 lefttop 字段值设置其坐标 , 分别对应 currentXcurrentY 值 ;

监听事件 : 监听 GestureDetector 组件的 onPanUpdate 事件 , 其回调方法是 void Function(DragUpdateDetails details) 类型的 方法 , 可以从 DragUpdateDetails 类型参数中获取当前 x , y 的移动距离 , 该距离需要与之前的距离累加 , 才能得到准确的坐标值 ;

在回调方法中调用 setState 方法 , 修改成员变量 currentXcurrentY , 从而修改 Positioned 组件的位置 , 以达到小球移动的目的 ;

代码示例 :

完整代码示例

完整代码示例 :

继续阅读Flutter手势交互 ( 跟随手指运动的小球 )

Windows git ssh报错“no matching host key type found. Their offer: ssh-rsa,ssh-dss”

Windows 下检出代码的时候报错:

解决:

前提: 在排除没有配置公钥的情况下。

  • Git的安装目录下 Git > etc > ssh 文件夹下找到 ssh_config 文件
  • 在文件末尾添加一下代码(注意,这里的 xxx.com 就是远程仓库地址或者ip,格式如:baidu.com)

参考链接


windows git ssh 方式提示 no matching host key type found. Their offer: ssh-rsa,ssh-dss

Dart 2.15更新后isolate应该这么用

序言

2021年的最后一天, Dart 官方发布了 dart 2.15 版本,该版本优化了很多内容,今天我们要重点说说 isolate 工作器。官方推文链接

在探索新变化之前,我们来回忆巩固一下 isolate 的使用。

isolate 的作用

问题:Flutter 基于单线程模式使用协程进行开发,为什么还需要 isolate

首先我们要明确 并行(isolate并发(future)的区别。下面我们通过简单的例子来进行说明 。Demo 是一个简单的页面,中间放置一个不断转圈的 progress 和一个按键,按键用来触发耗时方法。

  • 方式一: 我们将耗时操作使用 future 的方式进行封装

结论:使用 future 的方式来消费耗时操作,由于仍然是单线程在进行工作,异步只是在同一个线程的并发操作,仍会阻塞UI的刷新。

  • 方式二: 使用 isolate 创建新线程,避开主线程,不干扰UI刷新

结论:使用 isolate 实现了多线程并行,在新线程中进行耗时操作不会干扰UI线程的刷新。

isolate 的局限性,为什么需要优化?

iso 有两点较为重要的局限性。

  • isolate 消耗较重,除了创建耗时,每次创建还至少需要2Mb的空间,有OOM的风险。
  • isolate 之间的内存空间各自独立,当参数或结果跨 iso 相互传递时需要深度拷贝,拷贝耗时,可能造成UI卡顿。

isolate 新特性

Dart 2.15 更新, 给 iso 添加了组的概念,isolate 工作特征可简单总结为以下两点:

  • Isolate 组中的 isolate 共享各种内部数据结构
  • Isolate 组仍然阻止isolate 间共享访问可变对象,但由于 isolate 组使用共享堆实现,这也让其拥有了更多的功能。

官方推文中举了一个例子

工作器 isolate 通过网络调用获得数据,将该数据解析为大型 JSON 对象图,然后将这个 JSON 图返回到主 isolate 中。

Dart 2.15 之前:执行该操作需要深度复制,如果复制花费的时间超过帧预算时间,就会导致界面卡顿。

使用 Dart 2.15 :工作器 isolate 可以调用 Isolate.exit(),将其结果作为参数传递。然后,Dart 运行时将包含结果的内存数据从工作器 isolate 传递到主 isolate 中,无需复制,且主 isolate 可以在固定时间内接收结果。

重点:提供 Isolate.exit() 方法,将包含结果的内存数据从工作器 isolate 传递到主 isolate,过程无需复制。

附注: 使用 Dart 新特性,需将 flutter sdk 升级到 2.8.0 以上 链接

isolate中:exit 和 send 的区别及用法

Dart 更新后,我们将数据从 工作器 isolate(子线程)回传到 主 isolate(主线程)有两种方式。

  • 方式一: 使用 send

点击进入 send 方法查看源码注释,看到这样一句话:

结论:send 本身不会阻塞,会立即发送,但可能需要线性时间成本用于复制数据。

  • 方式二:使用 exit

官网 给出的解释如下:

结论:隔离之间的消息传递通常涉及数据复制,因此可能会很慢,并且会随着消息大小的增加而增加。但是 exit(),则是在退出隔离中保存消息的内存,不会被复制,而是被传输到主 isolate。这种传输很快,并且在恒定的时间内完成。

我们把上面 demo 中的 _entryPoint 方法做一下优化,修改如下:

总结:使用 exit() 替代 SendPort.send,可规避数据复制,节省耗时。

isolate

如何创建一个 isolate 组?官方给出的解释如下:

When an isolate calls Isolate.spawn(), the two isolates have the same executable code and are in the same isolate group. Isolate groups enable performance optimizations such as sharing code; a new isolate immediately runs the code owned by the isolate group. Also, Isolate.exit() works only when the isolates are in the same isolate group.

当在 isolate 中调用另一个 isolate 时,这两个 isolate 具有相同的可执行代码,并且位于同一隔离组。

PS: 小轰暂时也没有想到具体的使用场景,先暂放一边吧。

实践:isolate 如何处理连续数据

结合上面的耗时方法calculateEvenCountisolate 处理连续数据需要结合 stream 流的设计。具体 demo 如下:

参考链接


Dart 2.15 更新后 isolate 应该这么用

Flutter (2.10.1~3.3.9)/Xcode 13.2.1/macOS Big Sur 11.6.4在iPad Pro iOS 15.3.1/iPhone SE 3 iOS 16.1.1真机免费调试

系统与开发环境 Flutter (2.10.1~3.3.9)/Xcode 13.2.1(13C100)/macOS Big Sur 11.6.4/iPad Pro(Model A1673) iOS 15.3.1/iPhone SE 3 iOS 16.1.1

操作步骤

1. 苹果开发网站 注册或关联开发者账号,如果暂时不需要发布应用到 Mac App Store,只是在设备上调试应用,则不需要注册收费用户,只需要注册或者关联账号即可。具体可以查看官方介绍 选择会员资格

2. 在 iPad Promacbook Pro 登陆同一个注册的开发者的账号。

3. 通过 USB 数据线把 iPad Promacbook Pro 设备连接起来,如下图:

继续阅读Flutter (2.10.1~3.3.9)/Xcode 13.2.1/macOS Big Sur 11.6.4在iPad Pro iOS 15.3.1/iPhone SE 3 iOS 16.1.1真机免费调试