Implementowanie interfejsu IFilePromiseAdobe AIR 2 i wersje późniejsze Aby udostępnić obietnice plików reprezentujące zasoby, do których nie można uzyskać dostępu przez obiekt URLFilePromise, można zaimplementować interfejs IFilePromise w klasie niestandardowej. Interfejs IFilePromise definiuje metody i właściwości używane przez środowisko AIR do uzyskiwania dostępu do danych, które mają być zapisane w pliku po upuszczeniu obietnicy. Implementacja IFilePromise przekazuje do środowiska AIR następny obiekt, który udostępnia dane dla obietnicy pliku. Obiekt ten musi implementować interfejs IDataInput, którego środowisko AIR używa do odczytywania danych. Na przykład klasa URLFilePromise, która implementuje interfejs IFilePromise, używa obiektu URLStream jako dostawcy danych. Środowisko AIR może odczytywać dane synchronicznie lub asynchronicznie. Implementacja IFilePromise informuje, który tryb dostępu jest obsługiwany, zwracając odpowiednią wartość we właściwości isAsync. Jeśli obsługiwany jest asynchroniczny dostęp do danych, dostawca danych musi implementować interfejs IEventDispatcher i wywoływać niezbędne zdarzenia, takie jak open, progress i complete. W charakterze dostawcy danych dla obietnicy pliku można użyć własnej klasy niestandardowej lub jednej z klas wbudowanych:
Aby zaimplementować interfejs IFilePromise, należy udostępnić kod następujących funkcji i właściwości:
Wszystkie metody interfejsu IFilePromise są wywoływane przez środowisko wykonawcze podczas operacji przeciągania i upuszczania obietnicy plików. Zwykle logika aplikacji nie powinna bezpośrednio wywoływać żadnej z tych metod. Korzystanie z synchronicznego dostawcy danych w obietnicy plikówNajprostszym sposobem na zaimplementowanie interfejsu IFilePromise jest użycie obiektu synchronicznego dostawcy danych, takiego jak ByteArray lub synchroniczny obiekt FileStream. W poniższym przykładzie obiekt ByteArray jest tworzony, wypełniany danymi i zwracany po wywołaniu metody open(). package
{
import flash.desktop.IFilePromise;
import flash.events.ErrorEvent;
import flash.utils.ByteArray;
import flash.utils.IDataInput;
public class SynchronousFilePromise implements IFilePromise
{
private const fileSize:int = 5000; //size of file data
private var filePath:String = "SynchronousFile.txt";
public function get relativePath():String
{
return filePath;
}
public function get isAsync():Boolean
{
return false;
}
public function open():IDataInput
{
var fileContents:ByteArray = new ByteArray();
//Create some arbitrary data for the file
for( var i:int = 0; i < fileSize; i++ )
{
fileContents.writeUTFBytes( 'S' );
}
//Important: the ByteArray is read from the current position
fileContents.position = 0;
return fileContents;
}
public function close():void
{
//Nothing needs to be closed in this case.
}
public function reportError(e:ErrorEvent):void
{
trace("Something went wrong: " + e.errorID + " - " + e.type + ", " + e.text );
}
}
}
W praktyce synchroniczne obietnice plików mają ograniczone zastosowanie. Jeśli ilość danych jest niewielka, równie dobrze można utworzyć plik w katalogu tymczasowym i dodać tablicę z listą normalnych plików do schowka przeciągania i upuszczania. Jeśli zaś dane mają dużą objętość lub ich generowanie jest kosztowne obliczeniowo, proces synchroniczny będzie trwał długo. Długotrwały proces synchroniczny może zauważalnie wstrzymywać aktualizacje interfejsu użytkownika i sprawić, że aplikacja nie będzie odpowiednio reagować na działania użytkownika. Aby uniknąć tego problemu, można utworzyć asynchronicznego dostawcę danych sterowanego przez licznik czasu. Korzystanie z asynchronicznego dostawcy danych w obietnicy plikówW przypadku użycia obiektu asynchronicznego dostawcy danych właściwość isAsync obiektu IFilePromise musi być równa true, a obiekt zwrócony przez metodę open() musi implementować interfejs IEventDispatcher. Środowisko wykonawcze wykrywa kilka alternatywnych zdarzeń, tak że w charakterze dostawców danych można użyć różnych obiektów wbudowanych. Na przykład zdarzenia progress są wywoływane przez obiekty FileStream i URLStream, natomiast zdarzenia socketData są wywoływane przez obiekty Socket. Środowisko wykonawcze wykrywa odpowiednie zdarzenia ze wszystkich tych obiektów. Następujące zdarzenia sterują procesem odczytywania danych z obiektu dostawcy danych:
Dostawca danych powinien wywoływać te zdarzenia w następującej kolejności:
Uwaga: Wbudowane obiekty FileStream, Socket i URLStream automatycznie wywołują odpowiednie zdarzenia.
Poniższy przykład ilustruje tworzenie obietnicy pliku korzystającej z niestandardowego synchronicznego dostawcy danych. Klasa dostawcy danych rozszerza klasę ByteArray (w celu zaimplementowania interfejsu IDataInput) oraz implementuje interfejs IEventDispatcher. Przy każdym zdarzeniu licznika czasu obiekt generuje fragment danych i wywołuje zdarzenie progress, aby poinformować środowisko wykonawcze o dostępności danych. Po wygenerowaniu dostatecznej ilości danych obiekt wywołuje zdarzenie complete. package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.events.ProgressEvent;
import flash.events.TimerEvent;
import flash.utils.ByteArray;
import flash.utils.Timer;
[Event(name="open", type="flash.events.Event.OPEN")]
[Event(name="complete", type="flash.events.Event.COMPLETE")]
[Event(name="progress", type="flash.events.ProgressEvent")]
[Event(name="ioError", type="flash.events.IOErrorEvent")]
[Event(name="securityError", type="flash.events.SecurityErrorEvent")]
public class AsyncDataProvider extends ByteArray implements IEventDispatcher
{
private var dispatcher:EventDispatcher = new EventDispatcher();
public var fileSize:int = 0; //The number of characters in the file
private const chunkSize:int = 1000; //Amount of data written per event
private var dispatchDataTimer:Timer = new Timer( 100 );
private var opened:Boolean = false;
public function AsyncDataProvider()
{
super();
dispatchDataTimer.addEventListener( TimerEvent.TIMER, generateData );
}
public function begin():void{
dispatchDataTimer.start();
}
public function end():void
{
dispatchDataTimer.stop();
}
private function generateData( event:Event ):void
{
if( !opened )
{
var open:Event = new Event( Event.OPEN );
dispatchEvent( open );
opened = true;
}
else if( position + chunkSize < fileSize )
{
for( var i:int = 0; i <= chunkSize; i++ )
{
writeUTFBytes( 'A' );
}
//Set position back to the start of the new data
this.position -= chunkSize;
var progress:ProgressEvent =
new ProgressEvent( ProgressEvent.PROGRESS, false, false, bytesAvailable, bytesAvailable + chunkSize);
dispatchEvent( progress )
}
else
{
var complete:Event = new Event( Event.COMPLETE );
dispatchEvent( complete );
}
}
//IEventDispatcher implementation
public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
dispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference );
}
public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
{
dispatcher.removeEventListener( type, listener, useCapture );
}
public function dispatchEvent(event:Event):Boolean
{
return dispatcher.dispatchEvent( event );
}
public function hasEventListener(type:String):Boolean
{
return dispatcher.hasEventListener( type );
}
public function willTrigger(type:String):Boolean
{
return dispatcher.willTrigger( type );
}
}
}
Uwaga: Ponieważ klasa AsyncDataProvider w tym przykładzie rozszerza klasę ByteArray, nie może jednocześnie rozszerzać klasy EventDispatcher. Do zaimplementowania interfejsu IEventDispatcher wykorzystano wewnętrzny obiekt EventDispatcher, a wywołania interfejsu IEventDispatcher są przekazywane do tego wewnętrznego obiektu. Można również rozszerzyć klasę EventDispatcher i zaimplementować interfejs IDataInput (lub zaimplementować oba interfejsy).
Asynchroniczna implementacja interfejsu IFilePromise niemal nie różni się od implementacji synchronicznej. Główna różnica polega na tym, że właściwość isAsync zwraca wartość true, a metoda open() zwraca asynchroniczny obiekt danych: package
{
import flash.desktop.IFilePromise;
import flash.events.ErrorEvent;
import flash.events.EventDispatcher;
import flash.utils.IDataInput;
public class AsynchronousFilePromise extends EventDispatcher implements IFilePromise
{
private var fileGenerator:AsyncDataProvider;
private const fileSize:int = 5000; //size of file data
private var filePath:String = "AsynchronousFile.txt";
public function get relativePath():String
{
return filePath;
}
public function get isAsync():Boolean
{
return true;
}
public function open():IDataInput
{
fileGenerator = new AsyncDataProvider();
fileGenerator.fileSize = fileSize;
fileGenerator.begin();
return fileGenerator;
}
public function close():void
{
fileGenerator.end();
}
public function reportError(e:ErrorEvent):void
{
trace("Something went wrong: " + e.errorID + " - " + e.type + ", " + e.text );
}
}
}
|
|