You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin应用中自动登出定时器未按设定时间执行的问题求助

解决Xamarin中Device.StartTimer提前触发的问题

看起来你的定时器提前触发的问题大概率是多个定时器实例同时运行导致的——每次调用SignInAsync都会创建一个新的定时器,之前的定时器并没有被停止,它们各自独立计时,就会出现“还没到30分钟就触发”的假象(其实是旧的定时器到点了)。另外,Device.StartTimer在跨平台场景下的稳定性不如基于Task.Delay和CancellationToken的实现,我们可以从这两个方面来修复:

1. 全局管理定时器实例,避免重复创建

首先在MsalAuthService中添加一个静态的CancellationTokenSource,用来统一控制定时器的启停,确保同一时刻只有一个定时器在运行:

// MsalAuthService.cs
private static CancellationTokenSource _logoutTimerCts;
private static TimeSpan _timerInterval = TimeSpan.FromMinutes(30);

然后修改SignInAsync方法,在创建新定时器前先停止并清理旧的:

public async Task<bool> SignInAsync()
{
    try
    {
        // --- 原有登录逻辑 ---
        // login logic here
        // ... 登录成功后

        // 停止并清理旧定时器(如果存在)
        _logoutTimerCts?.Cancel();
        _logoutTimerCts?.Dispose();
        _logoutTimerCts = new CancellationTokenSource();

        // 用Task.Delay + CancellationToken实现更可靠的定时器
        _ = Task.Run(async () =>
        {
            while (!_logoutTimerCts.Token.IsCancellationRequested)
            {
                // 等待设定的间隔时间
                await Task.Delay(_timerInterval, _logoutTimerCts.Token);
                
                // 检查是否在等待过程中被取消,避免触发已取消的操作
                if (_logoutTimerCts.Token.IsCancellationRequested)
                    break;

                // 在主线程处理UI交互
                Device.BeginInvokeOnMainThread(async () =>
                {
                    await AutoLogout();
                });
            }
        }, _logoutTimerCts.Token);

        Application.Current.Properties["USERLOGINDATE"] = DateTime.Now;
        var accessToken = ApplicationPropertyProvider.GetProperty<string>(Commons.AUTH_TOKEN);
        return !string.IsNullOrEmpty(accessToken);
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.ToString());
        return false;
    }
}

2. 完善定时器的启停逻辑

当用户选择登出时,需要主动停止定时器:

public async Task LogoutAsync()
{
    // --- 原有登出逻辑 ---
    // 清空accessToken等登录状态
    ApplicationPropertyProvider.SetProperty<string>(Commons.AUTH_TOKEN, string.Empty);
    
    // 停止并清理定时器
    _logoutTimerCts?.Cancel();
    _logoutTimerCts?.Dispose();
    _logoutTimerCts = null;

    // 跳转到登录页
    App.Current.MainPage = new NavigationPage(new LoginPage());
}

在AutoLogout方法中,用户选择“继续会话”时不需要做额外操作——因为定时器是循环运行的,只要没被取消,会继续按30分钟间隔触发;如果选择“登出”,直接调用上面的LogoutAsync即可。

3. 可选:处理应用前后台切换的场景

如果你的应用需要在后台暂停计时、前台恢复计时,可以订阅App的Sleep和Resume事件:

// App.xaml.cs
private static TimeSpan _remainingTime = TimeSpan.FromMinutes(30);

protected override void OnSleep()
{
    // 暂停定时器时计算剩余时间
    if (MsalAuthService._logoutTimerCts != null)
    {
        var elapsed = DateTime.Now - (DateTime)Application.Current.Properties["USERLOGINDATE"];
        _remainingTime = TimeSpan.FromMinutes(30) - elapsed;
        MsalAuthService._logoutTimerCts.Cancel();
    }
}

protected override void OnResume()
{
    // 恢复定时器时使用剩余时间
    if (!string.IsNullOrEmpty(ApplicationPropertyProvider.GetProperty<string>(Commons.AUTH_TOKEN)))
    {
        MsalAuthService._logoutTimerCts?.Dispose();
        MsalAuthService._logoutTimerCts = new CancellationTokenSource();
        
        _ = Task.Run(async () =>
        {
            await Task.Delay(_remainingTime, MsalAuthService._logoutTimerCts.Token);
            if (!MsalAuthService._logoutTimerCts.Token.IsCancellationRequested)
            {
                Device.BeginInvokeOnMainThread(async () =>
                {
                    await new MsalAuthService().AutoLogout();
                });
            }
            // 恢复默认间隔,后续继续按30分钟触发
            _remainingTime = TimeSpan.FromMinutes(30);
            Application.Current.Properties["USERLOGINDATE"] = DateTime.Now;
        }, MsalAuthService._logoutTimerCts.Token);
    }
}

这样就能确保定时器的行为符合预期,不会出现提前触发的问题了。

内容的提问来源于stack exchange,提问作者ramdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 21:22:43