[Из песочницы] Запуск objective-c кода на Android устройствах
Начало истории Пришел мне на доработку проект написанный на cocos2d. Игра для детей, в которой необходимо собирать пазлы и учить слова. Работа как работа, но главная проблема заключалась в том, что до меня над проектом работала некая девушка из Индии. И тут у меня начался очень веселый период. Пример того, на что мне пришлось смотреть, что делать и чем все это закончилось, будет под катом.Индокод обеспечивает настроение на весь день //SelectPuzzle.h — (void)imagePickerController:(UIImagePickerController *)pickerView didFinishPickingMediaWithInfo:(NSDictionary *)info {
[[NSUserDefaults standardUserDefaults]setObject: nil forKey:@«cameraImage»]; UIImage *resiZedImage=[info objectForKey: UIImagePickerControllerOriginalImage]; UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation]; if (UIInterfaceOrientationIsPortrait (interfaceOrientation)) { isportrait=YES; resiZedImage=[self ImageResize: resiZedImage]; } else { isportrait=NO; resiZedImage=[self imageByCropping: resiZedImage toRect: CGRectMake (800,300,1006,1258)]; } [[NSUserDefaults standardUserDefaults]setInteger: preDefined6 forKey: imageIndex]; [[NSUserDefaults standardUserDefaults]setObject:[NSData dataWithData: UIImagePNGRepresentation (resiZedImage)] forKey:@«cameraImage»]; [[NSUserDefaults standardUserDefaults]synchronize]; [self performSelector:@selector (dismissCameraView:) withObject: pickerView afterDelay:1.0]; [[CCDirector sharedDirector]replaceScene:[PlayScene scene]]; }
//PlayScene.m -(void)init{ self = [super init]; if (self){ //… //… camera = YES; NSData* imageData = [[NSUserDefaults standardUserDefaults] objectForKey:@«cameraImage»]; UIImage* cameraImage = [[[UIImage alloc] initWithData: imageData]autorelease]; puzzleObject=[CCSprite spriteWithCGImage: cameraImage.CGImage key: nil]; //… //… } return self; } Однако пост не об ошибках, которые мне пришлось исправлять, а совсем о другой проблеме.Через несколько месяцев работы над проектом, когда основные проблемы были решены, заказчик попросил собрать билд под его Android девайс. Далее шел удивительный разговор о том, что первый разработчик должен был писать под cocos2d-x и приложение должно легко запускаться на любом устройстве. Решать проблему пришлось уже мне и мой выбор пал на Apportable.
Собственно тема Как утверждает официальный сайт, Apportable SDK — это система, которая позволяет запускать один и тот же xCode проект на iOS и Android устройствах. В первую очередь, это касается игр написаных на cocos2d. C портирование некоторых системных Core фреймворков есть проблемы.Наша игра была написана практически на чистом кокосе и только в двух местах использовались UIKit элементы.Ну что же. Приступим.Первое, что необходимо сделать — установить SDK на Mac. Для этого, следуя инструкциям на сайте, необходимо просто ввести в терминале
(echo; echo PATH=~/.apportable/SDK/bin:$PATH) >> ~/.bash_profile; source ~/.bash_profile SDK требует около 2 гб свободного места на диске и установленного xCode 5.
Пока все это скачивается, можно дописать в наш проект необходимый код.Сначала установим режим эммулирования экрана. Тут есть несколько варианта.Например: UIScreenIPhone3GEmulationMode — используется, если ваше приложение не будет поддерживать графику от retina устройств. Размер виртуального экрана 320 px/480 px, scale — 1.0UIScreenScaledAspectFitEmulationMode — растягивает ваше приложение в соответсвии с размером экрана Android устройства. Использует не retina графику.UIScreenBestEmulatedMode — растягивает ваше приложение в соответсвии с размером экрана Android устройства. Использует наиболее подходящую графику. Для корректной работы этого режима должна быть графика для всех вариантов iPhone и iPad экранов. Иначе может выглядеть совсем не best.
int main (int argc, char *argv[]) { @autoreleasepool { #ifdef ANDROID [UIScreen mainScreen].currentMode = [UIScreenMode emulatedMode: UIScreenAspectFitEmulationMode]; #endif int retVal = UIApplicationMain (argc, argv, nil, @«AppController»); return retVal; } } Все варианты typedef NS_ENUM (NSUInteger, UIScreenEmulationMode) { UIScreenIPhone3GEmulationMode, UIScreenIPhone4EmulationMode, UIScreenIPhone5EmulationMode, UIScreenIPadEmulationMode, UIScreenIPadRetinaEmulationMode, UIScreenAspectFitEmulationMode, UIScreenScaledAspectFitEmulationMode, UIScreenBestEmulationMode, UIScreenBestEmulatedMode = UIScreenBestEmulationMode, UIScreenNativeMode, UIScreenNativeRetinaMode, UIScreenBestNativeMode, }; Следующее что пришлось переделывать — позиции элементов.
[item setPosition: ccp (400, 600)]; на [item setPosition: ccp (puzzle.contentSize.width * 0.2, puzzle.contentSize.height * 0.772)]; Тоже самое при отслеживании нажатий.После этого надо перейти в терминале в папку с проектом и ввести:
apportable load Эта команда создаст apk файл, загрузит его на подключенное устройство, установит и запустит. В идеальном варианте. Он бывает не всегда. Я не буду рассматривать ошибки линковки файлов, они очень индивидуальны. Однако бывает ситуация когда apportable не видит какой-то определенный девайс. На офисе из 7 устройств, на которых я тестировал приложение, не смог установить на 3.Что делать в таком случае? Все очень просто. Apportable сначала создает apk файл и только потом пытается загрузить его на устройство. Значит этот файл где-то есть. Если более точно, то вот здесь: /Users/username/.apportable/SDK/Build/android-armeabi-debug/ProjectName В этой папке лежит apk файл и его можно просто перекинуть на девайс и установить. Для этого я использовал приложение Android File Transfer (в маке из коробки нельзя обращаться к памяти Android устройств).Все? Профит? К сожалению нет.Возникла проблема с фотокамерой. Камера вызывалась, делала фотографию, но при нажатии кнопки подтверждения, приложение падало. Проблема не гуглилась и обещала принести большие проблемы, но… Помощь пришла от наших android разработчиков. Я узнал, что такое манифест и пермишены.
Что же это? Для iOS разработчиков Аналог нашего info.plist. Манифест содержит в себе основные настройки, все права и конфигурацию.
И так. После создания билда с помощью apportable, в директории с проектом появилась папка ProjectName.aproj. В этой папке лежат конфигурационные файлы и файлы, которые не компилируются (например ресурсы).Нас интересует файл configuration.json. В нем можно изменить настройки компиляции проекта для android. Например, добавить пермишены. Для работы фотокамеры нужны права на запись.И изменение строки
«FEATURES»: [«opengles2», «landscape»] на «FEATURES»: [«opengles2», «landscape», «write_external_storage»] волшебным образом решило проблему.Также есть нюанс с локализацией имени приложения на экране устройства. Для каждого языка необходимо добавить файл strings.xml и положить в папку ProjectName/java/res/values-[lang_description]
strings.xml
//A list of java resource directories «java_res_dirs»: [«java/res»] Несколько моментов на последок:
Рамеров экранов у Android устройств очень много, и кое-где приходилось использовать следующие вставки: Скрытый текст #ifdef ANDROID CGRect frame = [UIScreen mainScreen].applicationFrame;//1.7031 — pad 1.778 — phone CGFloat ratio = frame.size.height / frame.size.width; if (ratio < 1.72) { x = 16; } else{ x = 29; } #endif Если в файле configuration.json не стоит флаг «portrait», а Device Orientation в настройках xCode проекта стоит галочка напротив «Portrait», то портретная ориентация поддерживаться будет. Будьте внимательны. Официальная документация docs.apportable.com/ В Apportable дописаны многие системные фреймворки. Официальная документация содержит мало информации об этом. Новые .h файлы с новыми доступными для android методами можно посмотреть в папке/Users/username/.apportable/SDK/sysroot/System/Library