2017-01-11 18 views
1

我正在开发一个聊天应用程序。在加载聊天消息并且消息可见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())?

回答

1

我发现在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(); 
     } 
    } 
} 
+0

这是使用'System.Threading.Timer',而不是'Xamarin.Forms.Device.StartTimer()',不应该在一个窗体项目中使用。只需使用'Device.StartTimer()'。 – BrewMate

+0

@BrewMate为什么不应该在Forms项目中使用它?我没有使用'System.Threading.Timer',它的工作完全正常,使用此代码 –

1

您可以尝试使用这个类,我发现,它涵盖了一些限制的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

是的,你可以用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; 
} 
+0

根据Xamarin文档,“虽然回调返回true,但定时器将保持循环。” - 这意味着定时器在完成后会重新启动。这不是我想要达到的效果 –

+0

此外,您的代码始终会调用'SendReadConfirmationAsync',如果在定时器运行时再次调用RefreshLocalData,情况就不会如此。我想你误解了我的问题 –

相关问题