読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

スマートフォンのスリープから復帰すると暫くSocket.ioの接続が復活しない

最初はイベントで復帰を判断して再接続すればいいだろう、ぐらいに軽く考えていたけど、思った以上にハマるポイントが多かった。

復帰の判断

android / chromeで試してたけど、resumeもpageshowもスリープからの復帰で発火されない。 いろいろ試したけど結局addEventListenerでの取得は不可能だと判断して、StackOverflowかどこかで見つけた以下の様なコードを使うことにした。

var last_update = new Date();
var TIMEOUT = 2000;
var INTERVAL = 2000;
setInterval(function () {
    var now = new Date();
    if (now.getTime() - last_update.getTime() > INTERVAL + TIMEOUT) {
        // ここで復帰時の処理
    }
    last_update = now;
}, INTERVAL);

スマートフォンではスリープ時にタイマーも止まるのを利用している。この例では、2秒ごとに前回のチェックから何秒経ったか確認し、2秒+αより大きければスリープしていた、と判断する。

再接続

再接続だからconnection.io.reconnect();だろうと考えて、reconnect()を呼ぶタイミングを工夫したり、最初のio.connect(ns, conf)でのconfの設定を見なおしたり、中のオブジェクトをいろいろいじったりもしたけれど、結局復帰から数十秒〜数分経たないと再接続しなかった。

結局切断して再接続すれば良かったんだけど、disconnectとconnectが複数あって判断に迷った。一応次のコードで動く。

var ns = '/foo';
var conn = io.connect(ns);

// 再接続
conn.disconnect();
conn.connect();

ネームスペースはconnに保存されているみたいだ。onでのイベントリスナも保持される。

全体のコード

var conn = io.connect(namespace);

var last_update = new Date();
var TIMEOUT = 2000;
var INTERVAL = 2000;
setInterval(function () {
    var now = new Date();
    if (now.getTime() - last_update.getTime() > INTERVAL + TIMEOUT) {
        conn.disconnect();
        conn.connect();
    }
    last_update = now;
}, INTERVAL);

感想

できたコードはシンプルだが、問題の切り分けを上手くできていなかったのでとても時間を使った。setInterval内で例外が発生していて、それによってなのかわからないが、setIntervalが途中で止まった。それを最初「スマートフォンではスリープするとsetInterval/setTimeoutがクリアされる」と勘違いしてしまった。この記事に書いたみたいに、再接続の問題とスリープからの復帰の問題を切り分けるべきだった。 ネームスペースとイベントリスナが保存されているかどうかの調査も同時に進めようとしてしまい、時間がかかった。次からは、問題と、その解決法によっては発生するかもしれない問題は、全て切り分けよう。

広告を非表示にする