web-dev-qa-db-ja.com

iPhone / Androidがスリープ状態になると、JavaScriptの実行(settimeoutなど)はどうなりますか?

IOSとAndroid=デバイスをターゲットとするjQuery Mobile Webアプリがあります。アプリケーションのコンポーネントはバックグラウンドタスクであり、定期的にa。)ローカルデータの変更とb。)接続をチェックしますサーバー。両方が真の場合、タスクは変更をプッシュします。

このタスクを実行するために、単純なsetTimeout()ベースの関数を使用しています。失敗または成功の各条件は、バックグラウンドタスクでsetTimeout()を呼び出し、30秒間隔で実行されるようにします。デバッグのために、最後のタスクランタイムのタイムスタンプでステータスdivを更新します。

どのデスクトップブラウザでも、これは問題なく機能します。ただし、iOSまたはAndroidでは、しばらくすると、タスクの実行が停止します。これはデバイスの省電力設定に関連しているのでしょうか?iOSがスタンバイになると、JavaScriptの実行が終了しますか?それが起こっているようです。

もしそうなら、再開するための最良の方法は何ですか?フックできるオンウェイクイベントはありますか?そうでない場合は、ユーザーの操作に依存するイベントにフックしない他のオプションがあります(バックグラウンドタスクを再開するためだけにページ全体をクリックイベントにバインドしたくない)。

37
Christopher

ブラウザページがフォーカスされていない場合、JavaScriptの実行がMobileSafariで一時停止しているようです。 setInterval()イベントが遅れている場合も、ブラウザがフォーカスされるとすぐに発生します。つまり、setInterval()を実行し続け、setInterval関数が通常よりも長くかかった場合にブラウザがフォーカスを失った/取り戻したと想定できるはずです。

このコードは、ブラウザのタブから切り替えた後、別のアプリから切り替えた後、スリープから復帰した後にアラートを出します。しきい値をsetTimeout()よりも少し長く設定すると、タイムアウトが発生してもタイムアウトしないと想定できます。

安全面を維持したい場合:タイムアウトID(setTimeoutによって返される)を保存し、これをタイムアウトよりも短いしきい値に設定します。これが発生した場合は、clearTimeout()およびsetTimeout()を再度実行します。

<script type="text/javascript">
var lastCheck = 0;

function sleepCheck() {
        var now = new Date().getTime();
        var diff = now - lastCheck;
        if (diff > 3000) {
                alert('took ' + diff + 'ms');
        }
        lastCheck = now;
}

window.onload = function() {
        lastCheck = new Date().getTime();
        setInterval(sleepCheck, 1000);
}
</script>

編集:これは、再開時に複数回連続してトリガーされる場合があるため、どうにかして処理する必要があります。 (Androidブラウザを一晩スリープさせた後、最大2回のalert()が発生しました。完全にスリープする前に、Javascriptが任意の時間に再開されたに違いありません。)

私はAndroid 2.2および最新のiOSでテストしました-どちらも、スリープ状態から再開するとすぐに警告します。

29
lunixbochs

ユーザーが別のアプリに切り替えるか、画面がスリープ状態になると、タイマーは、ユーザーがアプリに戻るまで(または画面が覚醒するまで)一時停止しているように見えます。

Phonegapには、状態をポーリングする代わりにリッスンできる resume イベントがあります(フォーカスが外れる前に何かしたい場合は pause イベントも同様です)。 deviceReadyが起動した後、それを聞き始めます。

_document.addEventListener("deviceready", function () {
    // do something when the app awakens
    document.addEventListener('resume', function () {
        // re-create a timer.
        // ...
    }, false);
}, false);
_

私はangularをphonegapとともに使用し、特定のタイムアウトを管理するサービスを実装していますが、基本的には、タイマーを設定し、タイマーをキャンセルし、最も重要なことに、タイマーを更新するオブジェクトを作成できます(更新は「再開」イベント中に呼び出されるものです)。

angularには、データをアタッチできるスコープとルートスコープがあります。タイムアウトはグローバルなので、ルートスコープにアタッチしますが、この例では簡単にアタッチしますドキュメントオブジェクトに適用する必要があります。ある種のスコープまたは名前空間に適用する必要があるためです。

_var timeoutManager = function () {
    return {
        setTimer: function (expiresMsecs) {
            document.timerData = {
                timerId: setTimeout(function () {
                    timeoutCallback();
                },
                expiresMsecs),

                totalDurationMsecs: expiresMsecs,
                expirationDate: new Date(Date.now() += expiresMsecs)
            };
        },

        updateTimer: function () {
            if (document.timerData) {
                //
                //  Calculate the msecs remaining so it can be used to set a new timer.
                //
                var timerMsecs = document.timerData.expirationDate - new Date();

                //
                //  Kill the previous timer because a new one needs to be set or the callback
                //  needs to be fired.
                //
                this.cancelTimer();

                if (timerMsecs > 0) {
                    this.setTimer(timerMsecs);
                } else {
                    timeoutCallback();
                }
            }
        },

        cancelTimer: function () {
            if (document.timerData && document.timerData.timerId) {
                clearTimeout(document.timerData.timerId);
                document.timerData = null;
            }
        }
    };
};
_

マネージャー関数にセットに渡す代わりにミリ秒のパラメーターを使用させることもできますが、これもangularサービスを書いた後のモデルです。操作は明確で、何かを行うのに十分なほど簡潔である必要があります。それらとあなた自身のアプリに追加します。

_var timeoutCallback = function () { console.log('timer fired!'); };
var manager = timeoutManager();
manager.setTimer(20000);
_

次のように、イベントリスナーで再開イベントを取得したら、タイマーを更新します。

_// do something when the app awakens
document.addEventListener('resume', function () {
    var manager = timeoutManager();
    manager.updateTimer();
}, false);
_

タイムアウトマネージャーには、タイマーを強制終了するためにいつでも使用できるcancelTimer()もあります。

4
Jeff LaFay

このクラスを使用できます github.com/mustafah/background-timer @jlafay answerに基づいて、次のように使用できます。

コーヒースクリプト

timer = new BackgroundTimer 10 * 1000, ->
    # This callback will be called after 10 seconds
    console.log 'finished'

timer.enableTicking 1000, (remaining) ->
    # This callback will get called every second (1000 millisecond) till the timer ends
    console.log remaining

timer.start()

javaScript

timer = new BackgroundTimer(10 * 1000, function() {
    // This callback will be called after 10 seconds
    console.log("finished");
});

timer.enableTicking(1000, function(remaining) {
    // This callback will get called every second (1000 millisecond) till the timer ends
    console.log(remaining);
});

timer.start();

それが役に立てば幸い、ありがとう...

2
Mustafah

ほぼどこでもサポートされている Page Visibility API(MDN) を使用する必要があります。ページまたはタブが再び表示されたかどうかを検出し、タイムアウトを再開したり、いくつかのアクションを実行したりできます。

0
sanjsanj