我正在开发一款聊天应用。在加载聊天消息并且消息可见5秒后,我想向服务器发送读取确认。这是我到目前为止所提出的:
public async void RefreshLocalData()
{
// some async code to load the messages
if (_selectedChat.countNewMessages > 0)
{
Device.StartTimer(TimeSpan.FromSeconds(5), SendReadConfirmation);
}
}
当调用RefreshLocalData()
时,我知道用户选择了另一个聊天,或者当前聊天中有新消息。因此,当调用RefreshLocalData()
时,我必须取消当前计时器以启动新计时器。
另一种情况,当我导航到另一个Page
时,我必须取消计时器。这没有问题,因为发生这种情况时会处理整个ViewModel
。
使用上面的代码,如果再次调用RefreshLocalData()
但尚未过了5秒的TimeSpan
,则该方法仍在执行。
有没有办法取消计时器(如果再次调用RefreshLocalData()
)?
答案 0 :(得分:6)
我在Xamarin论坛中找到了这个答案:https://forums.xamarin.com/discussion/comment/149877/#Comment_149877
我已经改变了一点以满足我的需求,这个解决方案正在运行:
public class StoppableTimer
{
private readonly TimeSpan timespan;
private readonly Action callback;
private CancellationTokenSource cancellation;
public StoppableTimer(TimeSpan timespan, Action callback)
{
this.timespan = timespan;
this.callback = callback;
this.cancellation = new CancellationTokenSource();
}
public void Start()
{
CancellationTokenSource cts = this.cancellation; // safe copy
Device.StartTimer(this.timespan,
() => {
if (cts.IsCancellationRequested) return false;
this.callback.Invoke();
return false; // or true for periodic behavior
});
}
public void Stop()
{
Interlocked.Exchange(ref this.cancellation, new CancellationTokenSource()).Cancel();
}
public void Dispose()
{
}
}
这就是我在RefreshLocalData()
方法中使用它的方式:
private StoppableTimer stoppableTimer;
public async void RefreshLocalData()
{
if (stoppableTimer != null)
{
stoppableTimer.Stop();
}
// some async code to load the messages
if (_selectedChat.countNewMessages > 0)
{
if (stoppableTimer == null)
{
stoppableTimer = new StoppableTimer(TimeSpan.FromSeconds(5), SendReadConfirmation);
stoppableTimer.Start();
}
else
{
stoppableTimer.Start();
}
}
}
答案 1 :(得分:3)
您可以尝试使用我找到的这个类,它涵盖了DeviceTimer的一些限制:
public class MySystemDeviceTimer
{
private readonly TimeSpan timespan;
private readonly Action callback;
private CancellationTokenSource cancellation;
public bool running { get; private set; }
public MySystemDeviceTimer(TimeSpan timespan, Action callback)
{
this.timespan = timespan;
this.callback = callback;
this.cancellation = new CancellationTokenSource();
}
public void Start()
{
running = true;
start(true);
}
private void start(bool continuous)
{
CancellationTokenSource cts = this.cancellation; // safe copy
Device.StartTimer(this.timespan,
() =>
{
if (cts.IsCancellationRequested)
{
running = false;
return false;
}
this.callback.Invoke();
return continuous;
});
}
public void FireOnce()
{
running = true;
start(false);
running = false;
}
public void Stop()
{
Interlocked.Exchange(ref this.cancellation, new CancellationTokenSource()).Cancel();
}
}
然后为了你的目的:
MySystemDeviceTimer计时器;
if (timer == null)
{
timer = new MySystemDeviceTimer(TimeSpan.FromSeconds(5), SendReadConfirmation);
timer.FireOnce();
}
else if (timer.running)
timer.Stop();
答案 2 :(得分:2)
是的,只要您返回Device.StartTimer()
以重复此功能,就可以使用true
。我通常通过我可以在ViewModel中控制的布尔变量来处理这个问题。如下所示:
bool shouldRun = true;
public async void RefreshLocalData()
{
// some async code to load the messages
if (_selectedChat.countNewMessages > 0)
{
Device.StartTimer(TimeSpan.FromSeconds(5), async() =>
{
await SendReadConfirmationAsync()
return shouldRun;
});
}
}
public async Task SendReadConfirmationAsync()
{
//Do some stuff
if(we want to stop call)
shouldRun = false;
}