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 -
按钮。由于问题是不确定的,您可能不得不偶尔切换按钮。我主要看到它发生在颜色变化之后。
我遇到了三个“症状”:
- 标签消失
- 按钮不响应其
Release
事件(即使在释放按钮后计数器仍继续计数)
- 发生分段错误(我看到它报告了上面代码的第 22 行和第 91 行)
添加更多 UI 元素似乎会加剧问题并导致错误发生得更快,这在模拟器和实际手表上都会发生。我缺少某种同步或线程问题吗?是什么导致了这些错误?
请使用Device.StartTimer
代替System.Threading.Timer
。
System.Threading.Timer
使用工作线程,所以它不应该直接访问 UI
要在 UI 线程上执行您的代码,您可以使用 Device.BeginInvokeOnMainThread
我正在尝试为 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 -
按钮。由于问题是不确定的,您可能不得不偶尔切换按钮。我主要看到它发生在颜色变化之后。
我遇到了三个“症状”:
- 标签消失
- 按钮不响应其
Release
事件(即使在释放按钮后计数器仍继续计数) - 发生分段错误(我看到它报告了上面代码的第 22 行和第 91 行)
添加更多 UI 元素似乎会加剧问题并导致错误发生得更快,这在模拟器和实际手表上都会发生。我缺少某种同步或线程问题吗?是什么导致了这些错误?
请使用Device.StartTimer
代替System.Threading.Timer
。
System.Threading.Timer
使用工作线程,所以它不应该直接访问 UI
要在 UI 线程上执行您的代码,您可以使用 Device.BeginInvokeOnMainThread