2017-01-11 13 views
1

私はチャットアプリを開発中です。チャットのメッセージが読み込まれ、メッセージが5秒間表示された後、私はサーバーに読み取り確認を送信したいと思います。これは私がこれまでに作ってみたものです:RefreshLocalData()が呼び出されるとタイマーが終了する前にキャンセルする方法

public async void RefreshLocalData() 
{ 
    // some async code to load the messages 

    if (_selectedChat.countNewMessages > 0) 
    { 
     Device.StartTimer(TimeSpan.FromSeconds(5), SendReadConfirmation); 
    } 
} 

は、私は別のチャットのいずれかが、ユーザが選択したか、新しいメッセージが現在のチャットのために入って来たことを知っています。だからRefreshLocalData()が呼び出されたら、私は現在のタイマーをキャンセルして新しいタイマーを開始する必要があります。

別の状況では、もう1つのPageに移動するとタイマーをキャンセルする必要があります。このとき、ViewModel全体が処理されるため、問題ありません。

上記のコードでは、RefreshLocalData()が再度呼び出されたが、TimeSpanの5秒がまだ終了していない場合、このメソッドはまだ実行されています。

タイマーをキャンセルする方法がありますか(RefreshLocalData()が再度呼び出された場合)?あなたの目的のためにその後

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(); 
    } 
} 

答えて

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

これはない 'Xamarin.Forms.Device.StartTimer()'とフォームプロジェクトで使用すべきではないため、 'System.Threading.Timer'を使用するためです。 'Device.StartTimer()'を使うだけです。 – BrewMate

+0

@ BrewMate Formsプロジェクトでこれを使用しないのはなぜですか?私は 'System.Threading.Timer'を使用していません。このコードでは全く問題なく動作しています –

1

あなたは私が見つけたこのクラスを使用して試すことができ、それはDeviceTimerの限界の一部をカバーしてい

MySystemDeviceTimerタイマー。

if (timer == null) 
    { 
     timer = new MySystemDeviceTimer(TimeSpan.FromSeconds(5), SendReadConfirmation); 
     timer.FireOnce(); 
    } 
    else if (timer.running) 
     timer.Stop(); 
2

ありがとうございますあなたがtrueを返す限り、関数repeatを返す限り、を返します。私は、通常、これを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のドキュメンテーションによれば、「コールバックが真を返しても、タイマーは繰り返し発生します。 - これは、タイマーが終了した後に再起動することを意味します。これは私が達成したいものではありません –

+0

また、あなたのコードは常に 'SendReadConfirmationAsync'を呼び出します。これは、タイマーの実行中に' RefreshLocalData'が再び呼び出される場合には当てはまりません。私はあなたが私の質問を誤解したと思う –

関連する問題