Как что-то сделать, при этом ничего не делая
Очередной загрузчик. Часть 2.
Запись от gloomyBrain размещена 05.12.2012 в 01:07
В предыдущей части мы закончили на том, что научились делать разные штуки последовательно или параллельно, по приоритетам или нет, но главное - по очереди. В этой части мы сделаем удобный (IMO) загрузчик каких-угодно-классных-штуковин на основе как раз очереди. Приступим.
Давайте начнем с описания сущности, которая умеет загружать наши какие-угодно-классные-штуковины. Для этого мы сделаем базовый интерфейс любого загрузчика IAssetLoader:
package nq.net.assets.loaders { import flash.events.IEventDispatcher; /** * Событие удачной загрузки ресурса */ [Event(name = "complete", type = "flash.events.Event")] /** * Событие неудачи при загрузке */ [Event(name = "cancel", type = "flash.events.Event")] /** * Интерфейс для всех загрузчиков ресурсов */ public interface IAssetLoader extends IEventDispatcher { /** * Начать загрузку ресурса * * @param url URL-адрес ресурса для загрузки */ function load(url:String):void; /** * Остановить загрузку */ function close():void; /** * Загруженные данные */ function get data():Object; } }
С загрузчиком все ясно, но нам этого мало. Для чего мы, спрашивается, писали очередь в предыдущей статье? Давайте сделаем задание на загрузку каких-угодно-классных-штуковин. Опять же, универсальное.
package nq.net.assets { import flash.events.Event; import flash.events.EventDispatcher; import nq.net.assets.loaders.IAssetLoader; import nq.net.assets.storage.IAssetBundle; import nq.utils.task.IAsyncTask; internal final class LoadAssetTask extends EventDispatcher implements IAsyncTask { private var _storage:IAssetBundle; private var _loader:IAssetLoader; private var _url:String; private var _priority:int; private var _mimeType:String; /** * @param storage Хранилище, в которое нужно положить результат * @param loader Загрузчик ресурса * @param url URL-адрес ресурса для загрузки */ public function LoadAssetTask(storage:IAssetBundle, loader:IAssetLoader, url:String, priority:int, mimeType:String) { super(); if (!storage) throw new ArgumentError("Параметр не должен быть нулевым!"); if (!loader) throw new ArgumentError("Параметр не должен быть нулевым!"); if (!url) throw new ArgumentError("Параметр не должен быть нулевым!"); _storage = storage; _loader = loader; _url = url; _priority = priority; _mimeType = mimeType; } private function onCancel(event:Event):void { this.stop(); super.dispatchEvent(new Event(Event.CANCEL)); } private function onComplete(event:Event):void { this.stop(); _storage.addAsset(_loader.data, _url); super.dispatchEvent(new Event(Event.COMPLETE)); } public function get url():String { return _url; } /* INTERFACE nq.utils.task.IAsyncTask */ /** * Остановить выполнение задания */ public function stop():void { _loader.removeEventListener(Event.COMPLETE, onComplete); _loader.removeEventListener(Event.CANCEL, onCancel); _loader.close(); } /** * Проритет задания */ public function get priority():int { return _priority; } public function get mimeType():String { return _mimeType; } /** * Начать выполнение задания */ public function start():void { _loader.addEventListener(Event.COMPLETE, onComplete); _loader.addEventListener(Event.CANCEL, onCancel); _loader.load(_url); } } }
- реализация интерфейса IAsyncTask (ведь загрузка - асинхронная операция)
- отправка событий об успешном завершении и/или провале загрузки
- метод get mimeType():String
Остановимся пока на последнем. Вообще MIME-типы описаны в нашей любимой занудной википедии, но в данном случае это не имеет значения. Мы можем вместо них использовать вообще любые строки произвольной длины и ширины. Главное - чтобы строка, возвращаемая этим методом, однозначна говорила нам о типе загружаемой штуковины. Да-да, именно благодаря этой строке мы будем выбирать нужный нам IAssetLoader для загрузки. Кстати, именно этим, по сути, и будет заниматься наш навороченный загрузчик.
Итак, очередь у нас есть, загрузчик тоже есть, чего недостает? Правильно, хранилища! Нам же нужно куда-то складывать все то добро, которое мы грузим в несколько потоков. По возможности постараемся сделать так, чтобы ресурсы можно было не только загрузить, но и выгрузить (чтобы лишнюю память не занимали когда не нужны). Довольно слов, будем выражать мысли кодом. Интерфейс хранилища ресурсов:
package nq.net.assets.storage { /** * Интерфейс для хранилища загружаемых ресурсов */ public interface IAssetBundle { /** * Добавить ассет в пакет ресурсов * * @param asset Добавляемый ассет * @param uri Уникальное имя ассета в этом пакете */ function addAsset(asset:Object, uri:String):void; /** * Имеется ли ассет с таким именем в пакете * * @param uri Уникальное имя ассета в этом пакете */ function hasAsset(uri:String):Boolean; /** * Удалить ассет по имени * * @param uri Уникальное имя ассета в этом пакете */ function removeAsset(uri:String):void; /** * Получить ассет по имени * * @param uri Уникальное имя ассета в этом пакете */ function getAsset(uri:String):Object; } }
package nq.net.assets.storage { /** * Класс хранилища загружаемых ресурсов */ public final class AssetBundle implements IAssetBundle { private var _hash:Object; public function AssetBundle() { super(); _hash = new Object(); } /* INTERFACE nq.net.assets.IAssetBundle */ /** * Добавить ассет в пакет ресурсов * * @param asset Добавляемый ассет * @param uri Уникальное имя ассета в этом пакете */ public function addAsset(asset:Object, uri:String):void { if (uri in _hash) throw new ArgumentError("Ассет с URI '"+uri+"' уже зарегистрирован!"); _hash[uri] = asset; } /** * Имеется ли ассет с таким именем в пакете * * @param uri Уникальное имя ассета в этом пакете */ public function hasAsset(uri:String):Boolean { return (uri in _hash); } /** * Удалить ассет по имени * * @param uri Уникальное имя ассета в этом пакете */ public function removeAsset(uri:String):void { if (!(uri in _hash)) throw new ArgumentError("Ассет с таким URI не зарегистрирован!"); delete _hash[uri]; } /** * Получить ассет по имени * * @param uri Уникальное имя ассета в этом пакете */ public function getAsset(uri:String):Object { if (!(uri in _hash)) throw new ArgumentError("Ассет с таким URI ("+uri+") не зарегистрирован!"); return _hash[uri]; } } }
- есть очередь, которая может выполнять любые задания последовательно и параллельно (TaskQueue)
- есть интерфейс загрузчика каких-угодно-классных-штуковин
- есть хранилище для штуковин, которые мы уже загрузили
- есть универсальное задание, которое берет загрузчик, загружает штуковину и складывает ее в хранилище
Чего нет? Нет поставщика загрузчиков. Помните, мы говорили, что волшебная строка с MIME-типом поможет нам выбрать загрузчик? Так вот, настало время написать фабрику, которая будет принимать строку с типом и в ответ возвращать нам загрузчик для этого типа. Это удобно и гибко - у нас всегда будет одно место, в котором указаны соответствия между типами и загрузчиками. Фабрика будет выглядеть примерно вот так:
package nq.net.assets.loaders { /** * Интерфейс для поставщика загрузчиков ресурсов */ public interface IAssetLoaderFactory { /** * Получить загрузчик по типу загружаемого ресурса * * @param url URL-адрес загружаемого ресурса * @param mimeType Тип MIME загружаемого ресурса */ function getLoader(url:String, mimeType:String = "application/octet-stream"):IAssetLoader; } }
Все, я устал и мне надоело. Это вторая статья за сегодня, так что пример 2 загрузчиков, пример фабрики и пример использования - во вложениях. Хватит с меня. А с вас - восхваления. Можно орден. Но лучше лексус, нафига мне орден, правильно?
PS
Буду рад пояснить что-либо в комментариях если кто-то дочитал этот пост но не разобрался как пользоваться.
Всего комментариев 9
Комментарии
05.12.2012 03:54 | |
Complete есть, cancel есть, а где fault? Или предполагается, что все работает идеально?
|
05.12.2012 10:57 | |
Предполагается что cancel - это "не получилось". Если мы сами отменяем загрузку, то мы знаем почему не получилось. Если не сами - то тоже знаем (методом исключения).
|
05.12.2012 19:36 | |
А пользователю что показывать? "Видимо что-то случилось"?
|
Последние записи от gloomyBrain
- Очередной загрузчик. Часть 2. (05.12.2012)
- Очередной загрузчик. Часть 1. (04.12.2012)
- Пул объектов (31.08.2011)
- Мания чисел (08.12.2010)
- Отправка файлов на сервер (допилил) (24.10.2010)