我使用 dart FFI 从本机端检索数据,并使用 flutter 显示数据CustomPaint
。
我ValueNotifier
用来控制CustomPaint
重绘。
代码:按速率轮询数据
使用状态类,我定期从本机端轮询数据,并将其分配给ValueNotifier
.
class _ColorViewState extends State<ColorView> {
ValueNotifier<NativeColor> _notifier;
Timer _pollTimer;
@override
void initState() {
// TODO: implement initState
super.initState();
ffiInit();
// initialize notifier
_notifier = ValueNotifier<NativeColor>(ffiGetColor().ref);
_pollTimer = Timer.periodic(Duration(milliseconds: 16), _pollColor);
}
_pollColor(Timer t) {
setState(() {
print('polling ...');
_notifier.value = ffiGetColor().ref;
print('polled: ${_notifier.value.r}, ${_notifier.value.g}, ${_notifier.value.b}');
});
}
....
}
请注意,我以大约 60fps 的速度进行轮询。
我将通知程序绑定到 CustomPaint 重绘
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
width: double.infinity,
height: double.infinity,
color: widget.clrBackground,
child: ClipRect(
child: CustomPaint(
painter: _ColorViewPainter(
context: context,
notifier: _notifier,
clrBackground: Color.fromARGB(255, 255, 0, 255)
)
)
)
);
}
代码:响应式重绘 CustomPaint
然后将 CustomPaint 的重绘绑定到ValueNotifier
,我用检索到的颜色绘制屏幕。
class _ColorViewPainter extends CustomPainter {
ValueNotifier<NativeColor> notifier;
BuildContext context;
Color clrBackground;
_ColorViewPainter({this.context, this.notifier, this.clrBackground})
: super(repaint: notifier) {
}
@override
bool shouldRepaint(_ColorViewPainter old) {
print('should repaint');
return true;
}
@override
void paint(Canvas canvas, Size size) {
print("paint: start");
final r = notifier.value.r;
final g = notifier.value.g;
final b = notifier.value.b;
print("color: $r, $g, $b");
final paint = Paint()
..strokeJoin = StrokeJoin.round
..strokeWidth = 1.0
..color = Color.fromARGB(255, r, g, b)
..style = PaintingStyle.fill;
final width = size.width;
final height = size.height;
final content = Offset(0.0, 0.0) & Size(width, height);
canvas.drawRect(content, paint);
print("paint: end");
}
}
然后我注意到在视觉上颜色更新的速率低于轮询。这可以通过同时查看我的日志记录和电话屏幕来观察,尽管重绘工作正常。
问题
我应该如何实现感知同步更新?
我还应该补充一点,本机后端模拟以 1 秒的间隔在红/绿/蓝之间切换颜色。
由于轮询更加频繁,我预计会以大约 1 秒的间隔看到相当稳定的颜色变化。但是现在颜色的变化间隔更长。有时 repaint 很少被调用,可能是几秒钟,而轮询始终返回相当稳定的数据更新。
更新
根据我的测试,我应该保留setState
,否则重绘只会停止。此外,通过将数据更新切换到飞镖地,我发现一切都按预期工作。所以它必须是本机端或 FFI 接口中的东西。这是在不涉及 FFI 时按预期工作的修改后的 dart 代码。
基本上我使用一个恒定的颜色集合并遍历它。
class _ColorViewState extends State<ColorView> {
ValueNotifier<NativeColor> _notifier;
Timer _pollTimer;
var _colors;
int _step = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
ffiInit();
// constant colour collection
_colors = [
[255, 0, 0],
[0, 255, 0],
[0, 0, 255]
];
_notifier = ValueNotifier<NativeColor>(ffiGetColor().ref);
_pollTimer = Timer.periodic(Duration(milliseconds: 1000), _pollColor);
}
_pollColor(Timer t) {
setState(() {
print('polling ...');
// _notifier.value = ffiGetColor().ref;
_notifier.value.r = _colors[_step][0];
_notifier.value.g = _colors[_step][1];
_notifier.value.b = _colors[_step][2];
print('polled: ${_notifier.value.r}, ${_notifier.value.g}, ${_notifier.value.b}');
if (++_step >= _colors.length) {
_step = 0;
}
});
}
在本机方面,我有一个生产者/消费者线程模型正在工作。生产者以固定速率循环遍历颜色集合。每当生产者 ping 消费者时,消费者就会得到它。
#include <cstdlib>
#include <ctime>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
#ifdef __cplusplus
#define EXTERNC extern "C" __attribute__((visibility("default"))) __attribute__((used))
#else
#define EXTERNC
#endif // #ifdef __cplusplus
struct NativeColor {
int r;
int g;
int b;
};
NativeColor* gpColor = nullptr;
NativeColor gWorker = {255, 0, 255};
// producer / consumer thread tools
std::thread gThread;
std::mutex gMutex;
std::condition_variable gConVar;
int gColors[][3] = {
{255, 0, 0},
{0, 255, 0},
{0, 0, 255}
};
int gCounter = 0;
int gCounterPrev = 0;
EXTERNC void ffiinit() {
if(!gpColor) {
gpColor = (struct NativeColor*)malloc(sizeof(struct NativeColor));
}
if(!gThread.joinable()) {
gThread = std::thread([&]() {
while(true) {
std::this_thread::sleep_for (std::chrono::seconds(1));
std::unique_lock<std::mutex> lock(gMutex);
gWorker.r = gColors[gCounter][0];
gWorker.g = gColors[gCounter][1];
gWorker.b = gColors[gCounter][2];
if(++gCounter == 3) {
gCounter = 0;
gCounterPrev = gCounter;
}
lock.unlock();
gConVar.notify_one();
}
});
}
}
EXTERNC struct NativeColor* ffiproduce() {
// get yellow
gpColor->r = 255;
gpColor->g = 255;
gpColor->b = 255;
std::unique_lock<std::mutex> lock(gMutex);
gConVar.wait(lock, [&]{
return gCounter > gCounterPrev;
//return true;
});
*gpColor = gWorker;
gCounterPrev = gCounter;
lock.unlock();
return gpColor;
}
ffiproduce()
绑定到飞镖端ffiGetColor()
函数。所以我假设这个消费者函数在主线程中工作。
所以我的一个想法是,C++ 端的线程协调可能会影响颤振通过CustomPaint
.
但我现在不知道如何证明这一点。