2017-01-11 21 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,情況就不會如此。我想你誤解了我的問題 –

相關問題