2009-07-25

【AIR】XMLを非同期に読み書きする【FileStream】

都合上,XMLを自由に読み書きする必要が出てきた.



そこで,FileStreamクラス(AIRで追加されたファイルの読み書き用クラス)を使い,

XMLのメソッド1つで,非同期に読み書きをするXMLManagerクラスを作ってみた.



package {
import flash.events.EventDispatcher;
import flash.events.OutputProgressEvent;
import flash.events.Event;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;

/** XML用クラス
* @auther kusamochi
* @extend EventDispatcher
*/
public class XMLManager extends EventDispatcher {
public static const READ_COMPLETE:String = "read_complete";
public static const READ_CLOSE:String = "read_close";

public static const WRITE_PROGLESS:String = "write_progress";
public static const WRITE_CLOSE:String = "write_close";

private var _xml:XML;  // [read]xmlファイルの実体
private var fileStream:FileStream;
private var _mode:String;

//コンストラクタ
public function XMLManager() {
_mode = "NormalMode";
}

// 明示的な参照解除メソッド
public function deleteReference():void {
if(fileStream) {
if(_mode == "ReadMode") {
fileStream.removeEventListener(Event.COMPLETE, readStreamCompleteHandler);
fileStream.removeEventListener(Event.CLOSE, readStreamCloseHandler);
}
else if(_mode == "WriteMode") {
fileStream.removeEventListener(OutputProgressEvent.OUTPUT_PROGRESS, writeStreamOutputProgressHandler);
fileStream.removeEventListener(Event.CLOSE, writeStreamCloseHandler);
}
}
}

// ファイル読み込み
public function readXML(file:File):void {
trace("XMLManager::readXML");
trace("Reading : " + file.nativePath);

_mode = "ReadMode";
fileStream = new FileStream();
fileStream.addEventListener(Event.COMPLETE, readStreamCompleteHandler);
fileStream.addEventListener(Event.CLOSE, readStreamCloseHandler);
fileStream.openAsync(file, FileMode.READ);
}

// ファイル出力
public function writeXML(file:File):void {
trace("XMLManager::writeXML");
trace("Writing : " + file.nativePath);

_mode = "WriteMode";
fileStream = new FileStream();
fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, writeStreamOutputProgressHandler);
fileStream.addEventListener(Event.CLOSE, writeStreamCloseHandler);
fileStream.openAsync(file, FileMode.WRITE);
fileStream.writeMultiByte(_xml.toXMLString(), File.systemCharset);
}

public function printXML():void {
trace(_xml.toXMLString());
}

// EventHandler ///////////////////////////////////////////////////////////
// Read  ----------------------------------------
// Readストリームがクローズ
private function readStreamCloseHandler(event:Event):void {
trace("XMLManager::readStreamCloseHandler");
fileStream.removeEventListener(Event.COMPLETE, readStreamCompleteHandler);
fileStream.removeEventListener(Event.CLOSE, readStreamCloseHandler);
_mode = "NormalMode";

// カスタムイベントを配信する
super.dispatchEvent(new Event(READ_CLOSE));
}

// 読み込み完了
private function readStreamCompleteHandler(event:Event):void {
trace("XMLManager::readStreamCompleteHandler");
var tmpStr:String = fileStream.readMultiByte(fileStream.bytesAvailable, File.systemCharset);
fileStream.close();
_xml = new XML(tmpStr);

// カスタムイベントを配信する
dispatchEvent(new Event(READ_COMPLETE));
}

// write --------------------------------------
// Writeストリームがクローズ
private function writeStreamCloseHandler(event:Event):void {
trace("XMLManager::writeStreamCloseHandler");
fileStream.removeEventListener(OutputProgressEvent.OUTPUT_PROGRESS, writeStreamOutputProgressHandler);
fileStream.removeEventListener(Event.CLOSE, writeStreamCloseHandler);
_mode = "NormalMode";

// カスタムイベントを配信する
super.dispatchEvent(new Event(WRITE_CLOSE));
}

// Write進行中
private function writeStreamOutputProgressHandler(event:OutputProgressEvent):void {
trace("XMLManager::writeStreamOutputProgressHandler");
if(event.bytesPending == 0){
fileStream.close();
}

// カスタムイベントを配信する
super.dispatchEvent(new OutputProgressEvent(WRITE_PROGLESS));
}

// getter ////////////////////////////////////////////////////
// XMLオブジェクトを返す(他のクラスからのアクセスに対応)
public function get xml():XML {
return _xml;
}

// mode
public function get mode():String {
return _mode;
}

// setter //////////////////////////////////////////////
public function set xml(setXML:XML) {
_xml = setXML;
}

}
}

FileStreamクラスのファイルオープンには,openメソッド,openAsyncメソッドの2つがある.

openメソッドを使うと,それ以降のFileStreamの処理は,その行の処理が終わるまで次の行を実行しない

でも,それだと,ロード対象のサイズが大きくなった場合などに遅延が起きてしまう.



それを避けたいのなら,openAsyncメソッドでファイルオープンする.

openAsyncにすると,各処理ごとにイベントを発行する.

つまり,FileStreamのオブジェクトにaddEventListenerしとけば,読み込み終わりとか判定できるわけです.



そこで,上記のXMLManagerでは,各イベントを利用してMXLの読み書きを行い,さらにカスタムイベントを配信する.

EventDispatcherを継承したのはこのため.





これ,作るときに気づいたんだが,

READモードだとcompleteイベント発生するのに,WRITEモードではcompleteイベント発生しないんだね.

最初気づかずにWRITEモードでもcompleteでファイルクローズしてたから,

イベント自体が発生してないからクローズされていないってことで,

上書き時にエラー出て散々ハマりましたw



しょうがないんで,OutputProgressイベント(出力中に定期的に発生するイベント)を使って,

bytesPending(残りの書き込みバイト数)がゼロになったときにクローズするようにしてみた.



0 件のコメント: