如何在计时器结束前取消

时间:2017-01-11 09:01:29

标签: xamarin timer xamarin.forms

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

3 个答案:

答案 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;
}