實作 IFilePromise 介面Adobe AIR 2 以及更新的版本 若要為無法使用 URLFilePromise 物件存取的資源提供檔案承諾,您可以在自訂類別中實作 IFilePromise 介面。IFilePromise 介面會定義 AIR 執行階段使用的方法和屬性,用於存取一旦檔案承諾投入之後,會被寫入檔案中的資料。 IFilePromise 實作會將另一個物件傳送至可以提供資料給檔案承諾的 AIR 執行階段。 這個物件必須實作 IDataInput 介面,AIR 執行階段會透過這個介面去讀取資料。 例如,URLFilePromise 類別 (它會實作 IFilePromise) 會使用 URLStream 物件當做資料提供者。 AIR 可以同步或非同步讀取資料。IFilePromise 實作會在 isAsync 屬性傳回適當的值,報告支援的存取模式。如果提供非同步資料存取,資料提供者物件必須實作 IEventDispatcher 介面以及傳送必要的事件,例如 open、progress 以及 complete。 您可以使用自訂類別,或者以下其中一個內建的類別,當做檔案承諾的資料提供者。
若要實作 IFilePromise 介面,您必須為以下的函數和屬性提供程式碼:
在涉及檔案承諾的拖放作業時,所有的 IFilePromise 方法都是由執行階段呼叫的。一般情況下,您的應用程式邏輯不應該直接呼叫任何這些方法。 在檔案承諾中使用同步資料提供者實作 IFilePromise 介面最簡單的方法就是使用同步資料提供者物件,例如 ByteArray 或同步的 FileStream。 在以下範例中,會建立 ByteArray 物件、填入資料並在呼叫 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 ); } } } 實際應用方面,同步檔案承諾應用面不廣。若資料量很少,只要在暫存目錄中建立檔案,然後將一般檔案清單陣列新增至拖放剪貼簿之中即可。在另一方面,若資料量很大或者產生資料時需要進行大量的計算,則需要長時間執行同步程序。長時間執行同步程序會封鎖 UI 更新而且封鎖時間相當長,讓您的應用程式看起來似乎沒有反應。若要避免這種問題,您可以建立一個由計時器驅動的非同步資料提供者。 在檔案承諾中使用非同步資料提供者當您使用非同步資料提供者物件時,IFilePromise isAsync 屬性必須是 true 而且 open() 方法傳回的物件必須實作 IEventDispatcher 介面。執行階段會偵聽多個替代事件,如此不同的內部物件就可以當做是資料提供者。例如,FileStream 和 URLStream 物件會傳送 progress 事件,而 Socket 物件會傳送 socketData 事件。執行階段會從這些所有的物件偵聽正確的事件。 以下事件會產生從資料提供者物件讀取資料的程序:
資料提供者應該按照以下順序傳送這些事件:
備註: 內建的物件、FileStream、Socket 以及 URLStream,會自動傳送正確的事件。
以下範例會使用自訂的非同步資料提供者來建立一個檔案承諾。 這種資料提供者類別會擴充 ByteArray (為了支援 IDataInput) 以及實作 IEventDispatcher 介面。在每一個計時器事件中,這個物件會產生一堆資料,然後傳送一個 progress 事件來通知執行階段資料可以取得。產生足夠的資料後,這個物件會傳送一個 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 ); } } } 備註: 因為本範例中的 AsyncDataProvider 類別擴充 ByteArray,所以它無法也擴充 EventDispatcher。 為了實作 IEventDispatcher 介面,這個類別會使用內部的 EventDispatcher 物件,然後將 IEventDispatcher 方法呼叫轉送至該內部物件。您也可以擴充 EventDispatcher 以及實作 IDataInput (或實作這兩個介面)。
非同步 IFilePromise 實作幾乎與同步實作完全相同。主要的差別在於 isAsync 會傳回 true 而 open() 方法會傳回非同步資料物件: 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 ); } } } |
|