移动端屏幕熄灭时如何启用setTimeout触发机制?
移动端屏幕熄灭后保持定时器触发令牌刷新的解决方案
核心问题在于:移动端系统为了省电,当应用进入后台或屏幕熄灭时,会挂起主线程的JS执行,setTimeout()这类定时器会被冻结,直到应用回到前台才会继续执行。要解决这个问题,需要借助后台独立线程或平台原生能力:
1. 使用Service Worker实现后台定时任务
Service Worker是独立于应用主线程的后台脚本,只要应用进程未被系统杀死,它可以在后台保持运行(不同平台有存活时间限制)。
实现步骤:
- 主线程注册Service Worker:
if ('serviceWorker' in navigator) { window.addEventListener('load', async () => { try { await navigator.serviceWorker.register('/sw.js'); } catch (error) { console.error('Service Worker 注册失败:', error); } }); } - 在Service Worker中设置定时器并处理令牌刷新:
// sw.js let refreshTokenTimer; function startTokenRefresh() { // 按令牌有效期设置刷新间隔,示例为30分钟 const refreshInterval = 30 * 60 * 1000; refreshTokenTimer = setInterval(async () => { try { const response = await fetch('/api/refresh-token', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ /* 刷新所需参数 */ }) }); const newToken = await response.json(); // 将新令牌同步到主线程 self.clients.matchAll().then(clients => { clients.forEach(client => { client.postMessage({ type: 'TOKEN_REFRESHED', token: newToken }); }); }); } catch (error) { console.error('令牌刷新失败:', error); } }, refreshInterval); } self.addEventListener('activate', () => { startTokenRefresh(); }); // 支持从主线程停止定时器 self.addEventListener('message', (event) => { if (event.data.type === 'STOP_REFRESH') { clearInterval(refreshTokenTimer); } }); - 注意:iOS Safari对Service Worker后台存活限制较严格(通常几分钟后会终止),Android Chrome相对宽松;避免设置过短的定时间隔,防止被系统判定为耗电应用。
2. 结合平台原生后台任务能力
如果你的应用是WebView嵌入的原生App,或者是PWA,可以借助平台原生API提升可靠性:
- Android:使用WorkManager原生组件设置周期性任务触发令牌刷新,再通过WebView的JS桥同步到前端;也可使用Web标准
Background SyncAPI,在Service Worker中监听sync事件实现后台同步。 - iOS:使用Background Tasks框架(原生)配置后台任务;PWA可借助iOS 14.5+支持的
Background FetchAPI,定期触发后台刷新(但无法精确控制触发时间)。
3. 引导用户配置后台权限
系统的后台限制是核心约束,引导用户开启对应权限可降低应用被挂起的概率:
- Android:引导开启「允许后台运行」「无限制数据访问」权限
- iOS:引导开启「后台App刷新」权限
4. 降级方案:按需刷新而非定时刷新
如果后台定时的可靠性无法保证,可调整刷新逻辑:
- 每次发起业务请求前,检查令牌是否即将过期,若过期则立即触发刷新
- 监听应用
visibilitychange事件,在应用从后台回到前台时,立即检查并刷新令牌
内容的提问来源于stack exchange,提问作者Janciu
相关产品推荐
相关产品推荐

