2008年12月29日月曜日

AdobeAIRで処理が全て完了してからアプリを終了する

AdobeAIR(Flex3+ActionScript3)で作成しているアプリASlimTimerで大改修(?)をしていました。

AdobeAIRで作成すると、サーバとのアクセス処理(URLLoader)は非同期で実行されるので処理中に画面をポチッと終了...ってされると、中途半端な所で終わっちゃったりします。
ASlimTimerはSlimTimerのサービスを使っているのですが、最近SlimTimerのアクセスが凄く重かったりもして、余計にマズい状態になりやすくなってたりして。。

そこで、今までは オンライン状態だったら
”SlimTimerのサービスにアクセスする”とだけしていた処理を、
  1. ローカルDBに保存する
  2. SlimTimerのサービスにアクセスする
  3. サービスから正常完了のレスポンスがあった場合にはDBから情報を削除する
という処理に変更し、さらにSlimTimerのサービスのアクセスを同期処理で順次行なって行くようにしてみました。
これによって、SlimTimerのサービスが落ちていた対策とともに、サービスが重かったら待機中に更新を溜め込み、集約できる更新は一つに集約するという仕組みになっています。

どこかのサービスのAPIを使ったマッシュアップものを作る場合には、最初からこぅいぅ観点は必要だったんでしょうねぇ。。。勉強になったわ。

そして、「アプリ終了時にはそれらの処理が完了するまで待ちたい!」ので、ちょっと調べる。

AdobeAIRのメイン画面 WindowedApplication の closing というイベントを使ってできるみたいです。

たとえば、画面の終了をキャンセルさせたい(閉じさせたくない!)場合には、
mxmlの方では、
<mx:WindowedApplication closing="onWindowClosing(event)"

ActionScriptの方では
private function onWindowClosing(event:Event):void {
event.preventDefault();
}

と書くといける(=画面が閉じない) みたい。
preventDefaultというのは、Swingでいう、AWTEvent#consumeと一緒って感じかなぁ。

じゃー終了時に、処理が完了しているかチェックして、
完了していなかったら一定時間待ってから再度チェックして、
完了したと同時に終了させる
という事をさせたい場合には、
private function onWindowClosing(event:Event):void {
event.preventDefault();
var intervalId:uint = setInterval(function():void {
if (!isExecuting()) { //これは自分で実装したチェックの関数
//処理が完了
clearInterval(intervalId); //Intervalをクリア
stage.nativeWindow.close(); //画面を閉じる
}
}, 200); //200ミリ秒毎
}
という感じでできそうです。
setIntervalというのは、flash.utilsのパブリック関数として定義されているものです。
setInterval(closure:Function, delay:Number, ... arguments):uint
ミリ秒単位で指定した間隔ごとに関数を実行します。


WindowedApplicationのクラスの場合、import flash.utils.setInterval; というのは書かなくてもコンパイルが通りますが、これは多分WindowedApplicationのクラスの方でimportされているんでしょうね。
# こぅいぅ所はちょっと分かりにくくなっちゃうから、私的には嫌な所なんですが。

stage.nativeWindow.close(); というのは画面を終了する処理になっています。
stageというのはWindowedApplicationから参照できるプロパティです。stageというのは、以前からFlashをバリバリ使っている方にはおなじみのもののようなのですが、私のように初心者の方はクラスを俯瞰してみた記事画面遷移についてで少し記述しているので、よければどうぞ。

....と思いきや、setIntervalのASDoc見てみると、もぅ推奨されていないみたいですね。flash.utils.Timer を使えと。さよですか。
では、書き直し。
private function onWindowClosing(event:Event):void {
event.preventDefault();
var timer:Timer = new Timer(200, 0);
timer.addEventListener(TimerEvent.TIMER, function(event:TimerEvent):void {
if (!isExecuting()) {
timer.stop();
stage.nativeWindow.close();
}
});
timer.start();
}

こんな感じ。たぶん。
• • •