Tizen Watch App 中的非确定性错误

Nondeterministic Errors in Tizen Watch App

我正在尝试为 Samsung Galaxy Watch 创建一个简单的计数器应用程序,它响应表圈并有 plus/minus 个按钮分别与 increment/decrement 计数器。当计数器值低于特定阈值时,它的颜色应该改变(例如,当它接近 0 时,从白色变为橙色再变为红色)。应该能够按住按钮,使计数器在稍微延迟后快速 increment/decrement,直到它们被释放。这个功能大部分都有效,除了我有时会在按住按钮时遇到不确定的错误。通过检查我的代码,应该没有任何问题,但也许我遗漏了什么。这是一个复制问题的 SCCM:

using System;
using System.Collections.Generic;
using System.Timers;
using Tizen.Wearable.CircularUI.Forms;
using Xamarin.Forms;

namespace RepeatableCounter
{
    class Program : global::Xamarin.Forms.Platform.Tizen.FormsApplication
    {
        protected override void OnCreate()
        {
            base.OnCreate();
            LoadApplication(new App());
        }

        static void Main(string[] args)
        {
            var app = new Program();
            Forms.Init(app);
            global::Tizen.Wearable.CircularUI.Forms.FormsCircularUI.Init();
            app.Run(args);
        }
    }

    class App : Application
    {
        public App()
        {
            MainPage = new MainPage { Value = 20, Thresholds = { (5, Color.Red), (10, Color.Orange) }};
        }
    }

    class MainPage : BezelInteractionPage, IRotaryEventReceiver
    {
        private int value;
        private int ticks;
        private readonly Label counter;
        private readonly Timer resetTicks;

        public MainPage() : base()
        {
            value = 0;
            ticks = 0;

            counter = new Label
            {
                Text = value.ToString(),
                FontSize = 32,
                BackgroundColor = Color.Transparent,
                HorizontalTextAlignment = TextAlignment.Center
            };
            RepeatButton plusButton = new RepeatButton
            {
                Text = "+",
                Delay = 500,
                Interval = 100,
                HorizontalOptions = LayoutOptions.Center,
                WidthRequest = 60
            };
            RepeatButton minusButton = new RepeatButton
            {
                Text = "\u2212",
                Delay = 500,
                Interval = 100,
                HorizontalOptions = LayoutOptions.Center,
                WidthRequest = 60
            };

            Content = new StackLayout
            {
                VerticalOptions = LayoutOptions.Center,
                Spacing = -24,
                Children = {
                    plusButton,
                    counter,
                    minusButton
                }
            };

            RotaryFocusObject = this;
            resetTicks = new Timer
            {
                Interval = 500,
                Enabled = false,
                AutoReset = false,
            };
            resetTicks.Elapsed += (sender, e) => ticks = 0;

            plusButton.Pressed += (sender, e) => Value++;
            plusButton.Held += (sender, e) => Value++;
            minusButton.Pressed += (sender, e) => Value--;
            minusButton.Held += (sender, e) => Value--;
        }
        public int Value
        {
            get { return value; }
            set
            {
                this.value = value;
                counter.Text = this.value.ToString();

                Color selected = Color.Default;
                foreach ((int threshold, Color color) in Thresholds)
                {
                    if (value <= threshold)
                    {
                        selected = color;
                        break;
                    }
                }
                counter.TextColor = selected;
            }
        }

        public int TickThreshold { get; set; } = 10;

        public int FastTickStep { get; set; } = 5;

        public IList<(int, Color)> Thresholds { get; set; } = new List<(int, Color)>();

        public void Rotate(RotaryEventArgs args)
        {
            if (args.IsClockwise)
            {
                if (ticks >= 0)
                    ticks++;
                else
                    ticks = 1;
                if (ticks <= TickThreshold)
                    Value++;
                else
                    Value += FastTickStep;
            }
            else
            {
                if (ticks <= 0)
                    ticks--;
                else
                    ticks = -1;
                if (ticks >= -TickThreshold)
                    Value--;
                else
                    Value -= FastTickStep;
            }

            resetTicks.Stop();
            resetTicks.Start();
        }
    }

    public class RepeatButton : Button
    {
        private readonly Timer timer;

        public RepeatButton() : base()
        {
            timer = new Timer
            {
                Enabled = false,
                AutoReset = true
            };
            timer.Elapsed += (sender, e) => {
                timer.Interval = Interval;
                Held.Invoke(timer, e);
            };

            Pressed += (sender, e) => {
                timer.Interval = Delay;
                timer.Start();
            };
            Released += (sender, e) => timer.Stop();
        }

        public double Delay { get; set; } = 100;

        public double Interval { get; set; } = 100;

        public event EventHandler Held;
    }
}

要重现该问题,请按住 + and/or - 按钮。由于问题是不确定的,您可能不得不偶尔切换按钮。我主要看到它发生在颜色变化之后。

我遇到了三个“症状”:

添加更多 UI 元素似乎会加剧问题并导致错误发生得更快,这在模拟器和实际手表上都会发生。我缺少某种同步或线程问题吗?是什么导致了这些错误?

请使用Device.StartTimer代替System.Threading.TimerSystem.Threading.Timer 使用工作线程,所以它不应该直接访问 UI

要在 UI 线程上执行您的代码,您可以使用 Device.BeginInvokeOnMainThread