Я использую dart FFI для извлечения данных с нативной стороны и показываю данные с флаттером 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}');
});
}
....
}
Обратите внимание, что я опрашиваю со скоростью около 60 кадров в секунду.
И я привязываю уведомитель к перерисовке 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)
)
)
)
);
}
Код: Repaint 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");
}
}
Затем я заметил, что визуально обновления цвета происходят с меньшей скоростью, чем при опросе. Это наблюдается при одновременном просмотре моего журнала и экрана телефона, хотя перекраска работает.
Вопрос
Как мне добиться воспринимаемых одновременных обновлений?
I Следует также добавить, что встроенная имитация серверной части переключает цвета между красным / зеленым / синим с интервалом в 1 секунду.
Поскольку опрос выполняется гораздо чаще, я ожидаю увидеть довольно стабильное изменение цвета примерно на 1- второй интервал. Но сейчас цвета меняются с большим интервалом. Иногда перерисовка вызывается очень редко, что может занять несколько секунд, при этом опрос возвращает довольно стабильные обновления данных.
Обновление
Согласно моему тесту, я должен оставить setState
, иначе перерисовка просто останавливается. Также переключив обновление данных на dart land, я обнаружил, что все работает, как ожидалось. Так что это должно быть что-то на нативной стороне или в интерфейсе FFI. Вот модифицированный код дротика, который работает должным образом, когда FFI не задействован.
В основном я использую коллекцию постоянного цвета и перебираю ее.
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;
}
});
}
На нативной стороне у меня есть модель потока производитель / потребитель работает. Производитель прокручивает коллекцию цветов с фиксированной скоростью. И потребитель получает это всякий раз, когда производитель проверяет связь.
#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
.
Но я понятия не имею, как это доказать на данный момент.