addEventListenerで登録した処理が複数回実行される問題
と言うものでしばしハマったのだが、原因はTitaniumのバグではなく仕様であり、自分のプログラムのやり方だった。
原因は同じイベントに複数のコールバックを設定できるため、同じ内容でaddEventListenerを複数回実行すると同じ処理が複数回走ってしまうというもの。
ハマりついでにいろいろ検証したのでaddEventListenerとremoveEventListenerの挙動を整理してみた。
イベントに複数のコールバックを登録する
同じイベントに対してaddEventListerを複数回呼び出すと、そのイベントに対するコールバックが複数登録される。
Ti.App.addEventListener("custom_event", function(e) { return Ti.API.info("ONE"); }); Ti.App.addEventListener("custom_event", function(e) { return Ti.API.info("TW0"); }); Ti.App.addEventListener("custom_event", function(e) { return Ti.API.info("THREE"); }); Ti.App.fireEvent("custom_event", {});
出力は以下のようになる。
[INFO] ONE [INFO] TWO [INFO] THREE
上書きを期待しているとバグの原因になるので注意。
また複数のオブジェクトに内容がちょっとだけ違うコールバックをループで登録するような場合も、間違えてループが二回以上回ってしまったりすると、同じ処理が何回も行われることになる。自分がやってしまったのはこちらのケースだった。
イベントに設定したコールバックをひとつ削除
addEventListenerでコールバックを名前のついた関数で指定した場合は、removeEventListenerに同じ関数を渡すことで削除できる。
callback1 = function(e) { return Ti.API.info("ONE"); }; callback2 = function(e) { return Ti.API.info("TW0"); }; callback3 = function(e) { return Ti.API.info("THREE"); }; Ti.App.addEventListener("custom_event", callback1); Ti.App.addEventListener("custom_event", callback2); Ti.App.addEventListener("custom_event", callback3); Ti.App.removeEventListener("custom_event", callback2); Ti.App.fireEvent("custom_event", {});
出力は以下のようになる。
[INFO] ONE [INFO] THREE
addEventListenerに無名関数を渡した場合は同じ内容の無名関数をremoveEventListenerに指定しても削除できない。
イベントに設定したコールバックをすべて削除
関数引数がなければすべてのコールバックが削除される。
という挙動をしたように思ったのだが、アプリが落ちるようだ。
Ti.App.removeEventListener("custom_event");
この状態でイベントが発火するとアプリが落ちる。
存在しないイベントを発火しても落ちない。アプリが落ちていたのは上記の第二引数にコールバックを指定してないremoveEventListenerのせいだったらしい。
コールバックをすべて削除する手段はないのかな。
参考までに
引き起こされた問題の実例も書いておく。
以下のような問題が起きていた。
- OptionDialogが二度ボタンをクリックしないと閉じない
- WebViewのリンクから発火させたイベントが二回実行される
OptionDialogの方は二回開かれていたというオチ。
本家のQAに同じようなケースに関する質問がいくつもあったので、そこそこハマりどころのようだ。
2011/02/06追記
removeEventListnerですべてのイベントを削除するという箇所を訂正。