【问题标题】:How to resume JavaScript timer on iOS8 web app after screen unlock?屏幕解锁后如何在 iOS8 Web 应用程序上恢复 JavaScript 计时器?
【发布时间】:2014-11-18 10:32:18
【问题描述】:

在 iOS8 上,此 HTML5 网络应用程序不会在屏幕锁定后恢复 js 计时器,如果网络应用程序处于活动状态并从主屏幕图标启动,则该应用程序会解锁。在 iOS7 上,计时器将在这种情况下继续。我需要在屏幕解锁后继续计时 - 有什么提示可以实现吗?

注意/请先使用 Safari 的“添加到主屏幕”通过共享按钮将网络应用程序添加到主屏幕。在 Safari 中运行页面不会导致上述问题。

<html>
<head>
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <title>Test</title>
    <script>
        var tim;
        function go() {
            tim = window.setInterval(action, 1000);
        }
        function action() {
            document.getElementById('x').innerHTML = new Date().getTime().toString();
        }
    </script>
</head>
<body onload="go()">
    <div id="x"></div>
</body>
</html>

【问题讨论】:

  • 屏幕解锁后似乎所有的 setTimeout 和 setInterval 都失败了
  • 比较 web worker 定时器和本地定时器来检测本地定时器是否停止。然后我们可以 document.write('please restart') 因为发出 window.location.reload() 不能修复计时器问题
  • 在此之后 websocket 连接也完全断开。
  • 我也遇到过类似的问题,但似乎影响更深远。例如,我有一个通常保存到主屏幕的网络应用程序。由于在一些测试设备上安装了 iOS 8,如果设备被锁定然后再次解锁,尽管我的 web 应用程序仍然处于打开状态并全屏,所有后续的 AJAX 请求都没有收到响应。通过检查 Apache 的 Web 日志,我可以看到后端服务器接收到请求并发送有效响应,但没有执行成功、失败和完成事件。
  • Apple 再次让事情变得困难! 您可以监控屏幕上任意位置的点击或任何可能从用户交互中触发的事件,以检查计时器是否正在运行。如果不是,则重新启动计时器、AJAX 连接、WebSockets 等。

标签: javascript html ios8


【解决方案1】:

我们最终解决的解决方案并没有解决IOS8中睡眠模式后所有损坏的功能,而只是解决了ajax requests/setTimeout/setInterval/requestAnimationFrame 及其各自的清除功能。

睡眠模式后其他问题:

  • -webkit-transition opacity 导致不可选择的区域
  • 在打开键盘时调用worker.postMessage 会永久杀死所有WebWorker
  • 窗口弹跳/平移可以杀死所有 WebWorker。
  • overflowchange/resize 事件不再起作用。您需要重新处理依赖于它们的任何逻辑。

制作一个半可行的解决方案:

  1. 覆盖 setTimeout/setInterval/requestAnimationFrame/clearInterval/clearTimeout/cancelAnimationFrame。所有这些功能都需要 postMessage 到 webworker,webworker 将相应地执行 setTimeout/setInterval。您可能还需要为这些功能设置暂停机制,但我并不是 100% 有必要。
  2. 重写 XMLHttpRequest 以便在调用 send 时,请求实际上是在 webworker 上执行的。
  3. 为所有 webworkers 创建一个暂停机制,以便主线程可以确定所有 webworkers 何时刷新了他们的消息(基本上需要防止 postMessage 在键盘启动时被任何 worker 调用)。
  4. 防止键盘在可编辑元素上弹出。在检测到可编辑元素上的点击/单击事件后,您需要手动调用焦点函数。
  5. 在打开键盘的可编辑元素上覆盖焦点功能。 Focus 现在将调用暂停工作者函数。在所有工作人员暂停后,调用焦点的浏览器实现。
  6. 向可编辑元素添加 focusout 事件侦听器,以便调用恢复工作者函数。

我的 github 上有一个半抽象版本可供其他人使用。我们在相当复杂/大型且严重依赖动画+ajax 的应用程序中使用 github 版本,几乎没有问题。

https://github.com/TaDaa/IOS8-FIX

【讨论】:

    【解决方案2】:

    安装 iOS 8.1.1 解决了这个问题

    【讨论】:

    • 这在 iOS 8.1.2 上似乎仍然是一个问题 :(
    • 已确认。 8.1.2 还在杀setInterval。
    • 一个苹果问题的答案永远不会用另一个苹果来解决。
    • 我仍然可以在 iOS 8.3 上看到这个问题!!其他人可以确认吗?
    【解决方案3】:

    这是我的解决方法的一个示例。

    Worker 脚本 backgrond.js

    setInterval(function () {
        postMessage(JSON.stringify({
            message: "tiktok",
            result: new Date().getTime()
        }));
    }, 500);
    

    页面脚本 - 在 cmets 2014-10-30 之后编辑

    var GUI = {
        iOS8: false,
        web_worker: null,
        tik: new Date().getTime(),
        tok: new Date().getTime(),
        init: function(){
            GUI.web_worker = new Worker("scripts/background.js");
            GUI.web_worker.onmessage = function (event) {
                var d = JSON.parse(event.data);
                if (d.message === 'tiktok') {
                    GUI.tik = d.result;
                    if (Math.abs(GUI.tik - GUI.tok) > (15 * 1000) && GUI.iOS8 ) {
                        GUI.web_worker.terminate();
                        document.write('Please restart app after locking the screen.  Apple are working to prevent this issue.');
                     }
                };
            }
            setInterval(function () { GUI.tok = new Date().getTime(); }, 1000);
            var ua = navigator.userAgent.toLowerCase();
            GUI.iOS8 = ua.indexOf("os 8_0") !==-1 || ua.indexOf("os 8_1") !==-1;
      }
    }
    
    GUI.init();
    

    【讨论】:

    • 附加说明 userAgent 检查现在在 iOS8.1 上不起作用,所以任何使用它的人都应该找到更好的方法来检测 ios8+(例如,我正在使用 uaParserJS)
    • 是我瞎了还是你真的在每条消息上都创建了重复计时器?
    • 这在 iOS 8.1 上不起作用(大概在 iOS 8 上),因为 postMessage 在用户平移屏幕时被阻止。所以这个脚本(即使是固定的)会误报。
    • @Andy 您对滚动问题的看法是正确的,因此我们将生产系统上的差异检查从 1.5 秒增加到 15 秒 ps/ setInterval 在配置 webworker 侦听器后调用一次
    • @ajayel 您在代码中的 onmessage 中设置了 setInterval,这对我来说似乎是一个放错位置的花括号。顺便说一句,根据您的解决方案,我制作了我的解决方案,其中考虑了 touchmove:gist.github.com/pronebird/e8b87fb81dde9b0127ac。但是我仍然认为这个解决方案是不可靠的,因为 setInterval 或 postMessage 可能由于任何原因而延迟,设备延迟或电源效率优化..
    【解决方案4】:

    对于 Ajax 请求的另一种解决方法(涉及服务器端工作),但在这里工作:iOS 8 Bug - OnUpdateReady never called again when device returns from sleep

    【讨论】:

      【解决方案5】:

      我尝试了修复,但它在我的 iphone4s 和 IOS8 上不起作用。

      我在我的版本手机上发现一个警报框在您锁定并退出应用程序后崩溃......基本上我放了一个警报框,上面写着当 settimeout 计时器停止使用屏幕更新时请关闭并重新启动应用程序onlclick...太乱了...这是一个可怕的错误!

      【讨论】:

        【解决方案6】:

        它已在 iOS 8.1.3 上修复。

        我正在使用 EaselJS 库,该库在内部使用 requestAnimationFrame 或 setTimeout 来更新刻度。在 iOS 8.0.x 上也有同样的问题,但在 iOS 8.1.3 上已修复。

        【讨论】:

          【解决方案7】:

          假设每次手机解锁时都会处理window.onload事件,试试这个:

          window.onload = function() { window.location.reload() }
          

          由于我目前没有设备来测试这个,这只是一个盲目的猜测。希望这对你有用。

          【讨论】:

          • 不幸的是,onload 在屏幕解锁时没有触发
          • 还请注意,即使您要从上面使用 ajayel 的解决方案,并尝试 window.location.reload() 而不是显示消息,重新加载后计时器仍然会损坏。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-24
          • 2015-11-25
          相关资源
          最近更新 更多