В этой статье я расскажу как создать простое нативное расширение для платформы iOS. А так же разберем как созданное расширение применяется в ActionScript проекте. Нам понадобятся компиляторы Adobe Flash Builder и Xcode. Подразумевается что вы знаете как создавать swc библиотеки и actionscript-приложения в Adobe Flash Builder. Если вы не знаете как это делается, советую почитать соответствующую литературу. Знаний по ObjectiveC/Xcode не требуется.
Разработка нативного расширения делится на три этапа:
В методе run класса HelloAne мы вызываем медом runHelloAne у созданного экземпляры ContextInterface. Далее FlashPlayr вызовет метод с именем runHelloAne в проекте Xcode. Собираем из полученного класса библиотеку helloaneLib.swc. Когда создаете библиотеку SWC необходимо в настройках проекта Flash Builder поставить галочку Include Adobe AIR libraries.
На предыдущем скрине вы наверняка обратили внимание на пункт AIR Native Extension в списке шаблонов проекта. Это шаблон который позволяет быстро создавать проект для компиляции статической библиотеки и сборки пакета ANE. Об этом шаблоне я подробно расскажу в последующих статьях. А пока попробуем разобраться со стандартными средствами :)
На следующем шаге указываем имя проекта helloane, и другую не очень важную на данный момент информацию. Нажимаем кнопку Next:
На последнем этапе выбираем каталог для проекта и сохраняем его:
Создав проект необходимо положить в каталог проекта файл FlashRuntimeExtensions.h и добавить его с помощью пункта меню File->Add files to "helloane". Файлик должен появится в проекте как указано на скриншоте.
По умолчанию Xcode компилирует результат во временный системный каталог. В своих проектах я настраиваю Xcode так что бы он компилировал результат в каталог build, рядом с каталогом приложения. Для этого кликните на название проекта(helloane) в левой части Xcode. А затем в центральной части Xcode кликаем на targets->helloane. В правой части Xcode откроется окно с настройками, кликаем на вкладку Build Settings и находим поле с Pre-configuration Build Products Path и пишем туда: $(PROJECT_DIR)/build/.
Разработку Xcode приложения для вашего нативного расширения можно условно разделить на 3 шага:
- Глобальная инициализация это два метода которые вызывает Flash Player в момент создания/уничтожения вашего нативного расширения. В нашем примере эти методы называются helloaneExtInitializer и helloaneExtFinalizer. Назовем их методами Глобальной инициализации.
- API компонента это те методы, которые вы будете вызывать из flash-приложения. В нашем случае один метод runHelloAne.
Возможно вы уже заметили в проекте два основных файла: helloane.h и helloane.m. Файл с расширением .h содержит прототипы всех функций которые мы будем использовать в файле с расширением .m. Рассмотрим листинг файла helloane.h:
helloaneExtInitializer
- void** extDataToSet : указатель на указатель (смешные они, ObjectiveC-разработчики :) ) данных для кастомной инициализации нативного расширения. Как пользоваться этим, пока не разобрался возможно расскажу в следующих статьях.
Разработка нативного расширения делится на три этапа:
- создание библиотеки SWC
- создание библиотеки Xcode
- сборка пакета ANE
Создание библиотеки SWC.
Ниже приведен листинг класса HelloAne.aspackage com.anedevelop.helloane { import flash.events.EventDispatcher; import flash.external.ExtensionContext; public class HelloAne extends EventDispatcher { private var context:ExtensionContext; public function HelloAne() { try { context = ExtensionContext.createExtensionContext('com.anedevelop.helloane', ''); } catch (error:Error) { // если context не создается - проверьте правильность указания // ID расширения: com.anedevelop.helloane // это же значение необходимо указать в файле extension.xml } } public function run():String { if (context != null) { return context.call('runHelloAne') as String; } return null; } } }Разберем его подробнее. В конструкторе класса мы создаем экземпляр класса ExtensionContext используя идентификатор расширения:com.anedevelop.helloane. Вы можете использовать любой идентификатор. Второй параметр в методе createExtensionContext может понадобится в том случае если вы хотите использовать в одном расширении разную логику. Мы не будем рассматривать эту возможность в рамках этой статьи.
В методе run класса HelloAne мы вызываем медом runHelloAne у созданного экземпляры ContextInterface. Далее FlashPlayr вызовет метод с именем runHelloAne в проекте Xcode. Собираем из полученного класса библиотеку helloaneLib.swc. Когда создаете библиотеку SWC необходимо в настройках проекта Flash Builder поставить галочку Include Adobe AIR libraries.
Создание библиотеки Xcode.
Создайте проект библиотеки Xcode выбрав пункт меню: File->New->Project. В появившемся окне в левой колонке кликаем на пункт Framework & Library раздела iOS, в правой части панели выбираем проект с именем Cocoa Touch Static Library и нажимаем кнопку Next:На следующем шаге указываем имя проекта helloane, и другую не очень важную на данный момент информацию. Нажимаем кнопку Next:
На последнем этапе выбираем каталог для проекта и сохраняем его:
Создав проект необходимо положить в каталог проекта файл FlashRuntimeExtensions.h и добавить его с помощью пункта меню File->Add files to "helloane". Файлик должен появится в проекте как указано на скриншоте.
По умолчанию Xcode компилирует результат во временный системный каталог. В своих проектах я настраиваю Xcode так что бы он компилировал результат в каталог build, рядом с каталогом приложения. Для этого кликните на название проекта(helloane) в левой части Xcode. А затем в центральной части Xcode кликаем на targets->helloane. В правой части Xcode откроется окно с настройками, кликаем на вкладку Build Settings и находим поле с Pre-configuration Build Products Path и пишем туда: $(PROJECT_DIR)/build/.
Разработку Xcode приложения для вашего нативного расширения можно условно разделить на 3 шага:
- Глобальная инициализация
- Локальная инициализация
- API компонента для вашего приложения
- Глобальная инициализация это два метода которые вызывает Flash Player в момент создания/уничтожения вашего нативного расширения. В нашем примере эти методы называются helloaneExtInitializer и helloaneExtFinalizer. Назовем их методами Глобальной инициализации.
- Локальная инициализация подразумевает
создание функций которые будут вызваны из методов глобальной
инициализации для создания/уничтожения вашего компонента. Возникает
вопрос зачем два метода инициализации? Честно говоря, на момент
написания это статьи я не знал ответа, если кто то сможет объяснить
зачем это нужно - милости просим к обсуждению.- API компонента это те методы, которые вы будете вызывать из flash-приложения. В нашем случае один метод runHelloAne.
Возможно вы уже заметили в проекте два основных файла: helloane.h и helloane.m. Файл с расширением .h содержит прототипы всех функций которые мы будем использовать в файле с расширением .m. Рассмотрим листинг файла helloane.h:
/* Здесь мы создаем прототип нашего приложения. */ #import <Foundation/Foundation.h> #import "FlashRuntimeExtensions.h" /////// Шаг первый. Глобальная инициализация кмопонента ************** // Создание компонента void helloaneExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet); // Уничтожение компонента void helloaneExtFinalizer(void* extData); /////// Шаг второй. Локальная инициализация ************** // Создание компонента void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet); // Уничтожение компонента void ContextFinalizer(FREContext ctx); /////// Шаг третий. API компонента для вашего приложения ************** // Метод который будет доступен из flash-приложения FREObject runHelloAne(FREContext ctx, void *data, uint32_t argc, FREObject argv[]);
Давайте попробуем понять значения аргументов используемых функций.
Описание этих функций на инглише можно почитать на сайте Adobe . А теперь по-русски.helloaneExtInitializer
- void** extDataToSet : указатель на указатель (смешные они, ObjectiveC-разработчики :) ) данных для кастомной инициализации нативного расширения. Как пользоваться этим, пока не разобрался возможно расскажу в следующих статьях.
- FREContextInitializer* ctxInitializerToSet : указатель на метод локальной инициализации
- FREContextFinalizer* ctxFinalizerToSet : указатель на метод локальной финализации
ContextInitializer
- void* extData : судя по всему это такая же шляпа что и в методе helloaneExtInitializer.
- const uint8_t* ctxType : тип нативного расширения, здесь передается значение которые вы передаете во втором аргументе в методе ExtensionContext.createExtensionContext в библиотеке SWC, в нашем случае используется пустая строка.
- FREContext ctx : объект обеспечивающий связь между xcode и flash приложениями. Например через него можно отправить вызов из xcode приложения во flash.
- uint32_t* numFunctionsToTest : указатель на количество наших api-методов, которые мы будем вызывать из flash проекта.
- const FRENamedFunction** functionsToSet : массив api-методов
runHelloAne
- FREContext ctx : ссылка на объект обеспечивающий связь между xcode и flash
- void *data : эта шляпа как то связана с данными которые передаются в extDataToSet и extData. Когда нибудь я разберусь как это работает :)
- uint32_t argc : Количество аргументов переданных из flash приложения
- FREObject argv[] : массив аргументов переданных из flash приложения.
Разобравшись с прототипами функций, давайте посмотрим на их реализацию в файле helloane.m
Разобравшись с прототипами функций, давайте посмотрим на их реализацию в файле helloane.m
// helloane.m // helloane // // Created by Serious Sam on 01.11.12. // Copyright (c) 2012 anedevelop.com. All rights reserved. // #import "helloane.h" /////// Шаг первый. Глобальная инициализация кмопонента ************** // Глобальная инициализация void helloaneExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet) { *extDataToSet = NULL; // передаем ссылки на наши методы инициализации и освобождения памяти *ctxInitializerToSet = &ContextInitializer; *ctxFinalizerToSet = &ContextFinalizer; } // Уничтожение компонента void helloaneExtFinalizer(void* extData) { } /////// Шаг второй. Локальная инициализация ************** // Создание компонента void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet) { static FRENamedFunction func[] = { // здесь можно через запятую передать сколько угодно функций { (const uint8_t*)"runHelloAne", NULL, runHelloAne } }; *numFunctionsToTest = sizeof(func) / sizeof(FRENamedFunction); *functionsToSet = func; } // Уничтожение компонента void ContextFinalizer(FREContext ctx) { return; } /////// Шаг третий. API компонента для вашего приложения ************** BOOL resultValue = NO; // Наш основной метод, который будет вызываться из flash приложения FREObject runHelloAne(FREContext ctx, void *data, uint32_t argc, FREObject argv[]) { // поочередно будет возвращать значение true и false resultValue = !resultValue; // создаем объект который необходимо вернуть после выполнения метода FREObject freResult; // присваиваем объекту result значение resultValue FRENewObjectFromBool(resultValue, &freResult); // возвращаем результат freResult return freResult; }Наше xcode-приложение готово. Нажимаем Cmd+B (или Product->Build), в каталоге build в каталоге вашего приложение должен появится файлик статической библиотеки: libhelloane.a
Сборка пакета ANE
Итак мы добрались до последнего этапа создания нативного расширения. В предыдущих двух этапах мы создали две библиотеки helloaneLib.swc и libhelloane.a. Теперь необходимо из этих двух библиотек создать пакет расширения ane. Делается это с помощью компилятора от Adobe adt, он работает в командной строке. Ниже приведен листинг shell-скрипта который собирает наш пакет ane:
#!/bin/sh AIR_SDK_PATH="~/sdks/AdobeAIRSDK_3.3/" ANE_NAME="hello.ane" SWC_FILE_NAME="helloaneLib.swc" STATIC_LIB_NAME="libhelloane.a" BUILD_DIR="./build/" mkdir $BUILD_DIR cp -f "extension.xml" $BUILD_DIR"extension.xml" cp -f "platformoptions.xml" $BUILD_DIR"platformoptions.xml" cp -f "../xcode/build/"$STATIC_LIB_NAME $BUILD_DIR cp -f "../asLib/bin/"$SWC_FILE_NAME $BUILD_DIR /usr/bin/unzip -o "$BUILD_DIR$SWC_FILE_NAME" -d "$BUILD_DIR" rm $BUILD_DIR"catalog.xml" pushd "$BUILD_DIR" "$AIR_SDK_PATH"/bin/adt -package -target ane "$ANE_NAME" extension.xml -swc "$SWC_FILE_NAME" -platform default library.swf -platform iPhone-ARM -platformoptions platformoptions.xml "$STATIC_LIB_NAME" library.swf popd rm $BUILD_DIR$SWC_FILE_NAME rm $BUILD_DIR$STATIC_LIB_NAME rm $BUILD_DIR"extension.xml" rm $BUILD_DIR"platformoptions.xml" rm $BUILD_DIR"library.swf"
Для работоспособности скрипта необходимо проверить следующее:
- AIR_SDK_PATH - путь к каталогу, где лежит AIR SDK
- Убедитесь что рядом с этим скриптом лежат файлы extension.xml и platformoptions.xml. Описание этих файлов будет ниже.
- Убедитесь что путь до файла статической библиотеки задан верно:
"../xcode/build/"$STATIC_LIB_NAME - Убедитесь что путь до библиотеки swc задан верно:
"../asLib/bin/"$SWC_FILE_NAME
<extension xmlns="http://ns.adobe.com/air/extension/3.1"> <id>com.anedevelop.helloane</id> <versionNumber>1</versionNumber> <platforms> <platform name="iPhone-ARM"> <applicationDeployment> <nativeLibrary>libhelloane.a</nativeLibrary> <initializer>helloaneExtInitializer</initializer> <finalizer>helloaneExtFinalizer</finalizer> </applicationDeployment> </platform> <platform name="default"> <applicationDeployment/> </platform> </platforms> </extension>Описание параметров:
- id - идентификатор расширения, значение должно совпадать со значением первого аргумента в методе ExtensionContext.createExtensionContext в библиотеке SWC.
- nativeLibrary - название статической библиотеки
- initializer - название метода глобальной инициализации, значение должно совпадать с методом глобальной инициализации, которое мы использовали в проекте Xcode.
- finalizer - название метода глобальной финализации в проекте Xcode
<platform xmlns="http://ns.adobe.com/air/extension/3.1"> <sdkVersion>6.0</sdkVersion> <description >Hello Ane extension</description> <copyright>Serious Sam (c) 10.2012</copyright> <linkerOptions> </linkerOptions> </platform>Здесь все понятно, кроме linkerOptions. Если в проекте Xcode вы используете дополнительные фреймворки (frameworks) то в разделе linkerOptions необходимо указать названия этих фреймворков, что бы они были принудительно внедрены в ваш пакет нативного расширения. Использование этого параметра мы рассмотрим когда будем разбирать пример нативного расширения для получения данных контактов из адресной книги устройства.
Использование нативного расширения в проекте flash
А теперь самое интересное - создадим проект flash с использованием нативного расширения: File->New->ActionScript Mobile Project. Вводим имя и переходим к следующему шагу. На этапе Mobile Settings отключаем галочки BlackBerry Tables OS и Google Android, т.к а данной статье мы разбираем разработку расширения только для платформы iOS.
Откройте свойства проекта, далее в левом списке кликаем на раздел ActionScript Build Path. В правой части окна выбираем вкладку Native Extensions. Нажимаем кнопку Add ANE и выбираем созданный ранее пакет hello.ane и нажимаем ОК что бы изменения вступили в силу.
Открываем свойства еще раз, раскрываем список ActionScript Build Packiging и выбираем пункт Apple iOS. В правой части экрана видим 4 вкладки:
- Digital signature
- Package Contents
- Entitlements
- Native Extensions
- Digital signature Здесь мы указываем путь к Apple-сертификату и Provisioning файлу. Получить их можно на сайте https://developer.apple.com/ios/manage/certificates/team/index.action. Получение сертификата Apple задача не тривиальная, по крайней мере так было для меня :). Если кому интересно расскажу подробнее об этом в отдельной статье.
- Package Contents Здесь мы указываем список файлов которые мы хотим загрузить в наше приложение. Имейте ввиду что загружать SWF файлы с кодом ActionScript нельзя, допускается только загрузка swf файлов с анимацией на Timeline. Я глубоко опечалился узнав об этом ограничении. Но ничего не поделаешь - это политика безопасности.
- Entitlements Пустая бесполезная страница.
- Native Extensions Здесь мы видим список используемых нативных расширений. Поставьте галочку Package, если она не установлена. Здесь же, в поле Apple iOS SDK не забудьте указать путь до каталога где лежит используемый в проекте Xcode SDK:
Обратите внимание что если вы подключили нативное расширение, то подключать SWC библиотеку не обязательно(!). Т.к. весь код из этой библиотеки лежит в пакете нативного расширения.
С настройками покончено, теперь приступим к написанию кода:
package { import com.anedevelop.helloane.HelloAne; import flash.display.Sprite; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFormat; public class testAne extends Sprite { private var textField:TextField; private var ane:HelloAne; public function testAne() { super(); // textField = new TextField(); textField.width = 200; textField.height = 40; textField.border = true; textField.background = true; textField.defaultTextFormat = new TextFormat(null, 30, 0xff0000, true); this.addChild(textField); // this.stage.addEventListener(MouseEvent.CLICK, stageClickHandler); } private function stageClickHandler(event:MouseEvent):void { if (ane == null) { ane = new HelloAne(); } textField.text = "result: "+ane.run(); } } }Здесь все максимально просто. Создаем объект класса HelloAne из созданной ранее библиотеки SWC, и вызываем метод run(). В текстовое поле поочередно должны выводиться значение true и false.
Ну вот и все
Надеюсь статья была полезна. Скачать исходники можно по ссылке. Если у вас что то не получилось - буду рад помочь, пишите в коментах
Можно ли сделать подобную hello.ane под MacOs?
ОтветитьУдалитьДля реализации подобного расширения под MacOS, если я не ошибаюсь, необходимо:
ОтветитьУдалить1) добавить платформу в файле extension.xml
2) в командной строке "$AIR_SDK_PATH"/bin/adt -package добавить новую платформу -platform ...
Постараюсь в ближайшее время собрать рабочий проект.
Http://anedevelop.blogspot.ru/2012/12/hello-ane-for-mac-macos.html : о том, как создавать нативное расширение для платформы MacOS. Надеюсь статья окажется полезной.
ОтветитьУдалитьКруто! Спасибо.
ОтветитьУдалитьДолго искал где разжуют все.