В августе на Intel Developer Forum в Сан-Франциско мы представили нативное мобильное приложение для iPаd для мониторинга пациентов, разработанное с помощью платформы Intel Multi-OS Engine. Приложение предоставляет данные о наиболее важных параметрах состояния пациента, подключаясь к прикроватным мониторам по WiFi-сети (более подробно о самом приложении и его функционале можно почитать на нашем сайте).
В данной статье мы поделимся опытом использования платформы Intel Multi-OS Engine, которая позволяет разрабатывать нативные приложения для iOS на Java.
В том случае, когда требуется разработать приложения как для Android, так и для iOS, возможность использовать Java для разработки приложений для iOS позволяет экономить время и ресурсы на разработку.
Основные преимущества работы с Intel Multi-OS Engine
UIElements
Основываясь на опыте, можно сказать, что основное положительное свойство платформы Multi OS-Engine — это возможность работать с нативными iOS UI элементами, практически так же, как в XCode. Все элементы, необходимые для разработки приложения, уже имелись в платформе, и, соответственно, не потребовалось добавлять что-то дополнительно. Каждый элемент полностью описан: присутствуют все характеристики и методы, что упрощает работу с платформой разработчикам, имеющим опыт работы с iOS.
Правильная организация приложения
В связи с тем, что данная платформа в дальнейшем позволит разрабатывать приложения сразу для Android и iOS, необходимо очень аккуратно подходить к разработке архитектуры приложения, правильно разделять общие и UI specific функциональности. Это позволяет строить очень точные и легкие приложения и организовывает разработчика.
Возможности Java
Преимуществом данной платформы является то, что она позволяет реализовать большинство возможностей Java. Например, рассмотрим работу с потоками и файлами:
Thread thread = new Thread() { public void run() { } }; thread.start(); private void openFileToRead() { String fileId = "hb"; NSBundle mainBundle = NSBundle.mainBundle(); String pathToFile = mainBundle.pathForResourceOfType(fileId, "dat"); File file = new File(pathToFile); FileInputStream fis = null; try { fis = new FileInputStream(file); DataInputStream dis = new DataInputStream(fis); //что-то делаем с input stream } catch (IOException e) { e.printStackTrace(); } }
В процессе работы мы проводили дополнительные исследования по работе с сетью и выяснили, что отлично работает фреймворк Retrofit в связке с okHttp.
Удобно и быстро пишем iOS специфичную часть на Java
Дополнительно можно использовать ObjC-подход для запуска в бэкграунде. В нашем случае при старте приложения мы запускали процесс чтения файлов:
@Override @Selector("application:didFinishLaunchingWithOptions:") public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) { performSelectorInBackgroundWithObject(new SEL("initQueueDispatcher"), null); return true; } @Selector("initQueueDispatcher") @Generated public void initQueueDispatcher() { QueueDispatcher.sharedQueueDispatcher().initQueue(); }
Запуск отдельных функций в главном потоке тоже не является проблемой:
public void heartRate(PatientRealData data) { performSelectorOnMainThreadWithObjectWaitUntilDone(new SEL("updatePatientData:"), } @Selector("updatePatientData:") @Generated public void updatePatientData(PatientRealData data) { mHrLabel.setText(String.valueOf(data.getHeartRate())); }
Доступ к ресурсам аналогичен с iOS API: чтобы получить изображение «alarm_on.png» из ресурсов и назначить его кнопке, достаточно выполнить следующее:
UIImage image = UIImage.imageNamed("alarm_on"); mAlarmButton.setImageForState(image, UIControlState.Normal);
Очень удобно, что синтаксис работы с iOS API на Java практически не отличается от оригинала на ObjC. К примеру, меню, основанное на UITableViewController, добавляли таким образом:
@com.intel.inde.moe.natj.general.ann.Runtime(ObjCRuntime.class) @ObjCClassName("PatientsTableVC") @RegisterOnStartup public class PatientsTableVC extends UITableViewController { static { NatJ.register(); } @Generated("NatJ") @Owned @Selector("alloc") public static native PatientsTableVC alloc(); @Generated("NatJ") @Owned @Selector("init") public native PatientsTableVC init(); @Generated("NatJ") protected PatientsTableVC(Pointer peer) { super(peer); } private ArrayList<PatientInfo> mPatients = new ArrayList<PatientInfo>(); @Selector("prefersStatusBarHidden") @Override public boolean prefersStatusBarHidden() { return true; } @Selector("viewDidLoad") @Override public void viewDidLoad() { setTitle("Select patient:"); } @Selector("numberOfSectionsInTableView:") @Override @NInt public long numberOfSectionsInTableView(UITableView tableView) { return 1; } @Selector("tableView:numberOfRowsInSection:") @Override @NInt public long tableViewNumberOfRowsInSection(UITableView tableView, long section) { return mPatients.size(); } @Selector("tableView:cellForRowAtIndexPath:") @Override public UITableViewCell tableViewCellForRowAtIndexPath(UITableView tableView, NSIndexPath indexPath) { String reusableId = "patientCell"; UITableViewCell cell = (UITableViewCell) tableView.dequeueReusableCellWithIdentifierForIndexPath(reusableId, indexPath); PatientInfo patient = mPatients.get((int) indexPath.row()); cell.textLabel().setText(patient.description()); return cell; } @Selector("prepareForSegue:sender:") @Generated public void prepareForSegueSender(UIStoryboardSegue segue, NSObject sender) { NSIndexPath indexPath = tableView().indexPathForSelectedRow(); PatientInfo patient = mPatients.get((int) indexPath.row()); MainMonitorVC controller = (MainMonitorVC) segue.destinationViewController(); controller.setPatient(patient); } }
Multi-OS Engine Plugin для Android Studio
Серьезным преимуществом платформы от Intel является «глубина» интеграции плагина Multi-OS Engine в Android Studio. Можно практически всю разработку проводить в Android Studio. При этом можно с легкостью настроить проект на работу сразу с двумя платформами. Для этого достаточно настроить конфигурации для запуска Android- и iOS-версии и переключаться между ними, просто выбрав нужную:
Подключение графиков
Для реализации отображения «бегущих» графиков были использованы наработки по работе с OpenGL, написанные на C. Для их использования был написан UIWaveFormVC контроллер на ObjC, где уже был добавлен C-код. Данный контроллер по входным данным отрисовывает на OpenGL View соответствующие точки с заданной скоростью и цветом.
UIWaveFormVC.h @interface UIWaveFormVC : GLKViewController @property (nonatomic, strong) DPSampleQueue * inputQueue; - (void)setDataQueue:(DPSampleQueue *) dataQueue; - (void)setWaveColor:(UIColor *)waveColor; - (void)setSampleFreq:(float)sampleFreq; UIWaveFormVC.m #import "UIWaveFormVC.h" @interface UIWaveFormVC () @property (strong, nonatomic) EAGLContext * context; @end - (void)setDataQueue:(DPSampleQueue *) dataQueue { self.inputQueue = dataQueue; } - (void)viewDidLoad { [super viewDidLoad]; self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; if (!self.context) NSLog(@"Failed to create ES context"); } - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { // сложная логика рисования графиков }
Далее для использования данного класса внутри Multi OS-Engine нам необходимо сгенерировать для него «обертку», то есть осуществить биндинг Java к ObjC:
UIWaveFormVC.java @com.intel.inde.moe.natj.general.ann.Runtime(ObjCRuntime.class) @ObjCClassName("UIWaveFormVC") @RegisterOnStartup public class UIWaveFormVC extends GLKViewController { @Generated("NatJ") protected UIWaveFormVC(Pointer peer) { super(peer); } @Selector("setDataQueue:") @Generated public native void setDataQueue(DPSampleQueue dataQueue); @Selector("setWaveColor:") @Generated public native void setWaveColor(UIColor waveColor); @Selector("setSampleFreq:") @Generated public native void setSampleFreq(float sampleFreq); static { NatJ.register(); } }
Далее мы просто добавляем UIWaveFormVC в логику экранов в MainUI.storyboard.
Затем вся работа проводится уже непосредственно из Java. Для передачи данных нашему UIWaveFormVC мы объявляем метод prepareForSequeSender(), который позволяет получить экземпляр класса контроллера до его отображения и передать ему данные.
@com.intel.inde.moe.natj.general.ann.Runtime(ObjCRuntime.class) @ObjCClassName("MainMonitorVC") @RegisterOnStartup public class MainMonitorVC extends UIViewController { static { NatJ.register(); } @Selector("alloc") public static native MainMonitorVC alloc(); @Selector("init") public native MainMonitorVC init(); @Generated("NatJ") protected MainMonitorVC(Pointer peer) { super(peer); } private QueueDispatcher mQueueDispatcher = null; @Selector("prepareForSegue:sender:") @Generated public void prepareForSegueSender(UIStoryboardSegue segue, NSObject sender) { if (segue.identifier() == null) return; UIWaveFormVC controller = (UIWaveFormVC) (segue.destinationViewController()); controller.setDataQueue(sharedQueueDispatcher().queueWithID(segue.identifier())); controller.setSampleFreq(SAMPLE_FREQ); controller.setWaveColor(WAVE_GREEN); } private QueueDispatcher sharedQueueDispatcher() { if (mQueueDispatcher == null) { mQueueDispatcher = QueueDispatcher.sharedQueueDispatcher(); mQueueDispatcher.startDataLoading(); } return mQueueDispatcher; } }
Рекомендации по улучшению Intel Multi-OS Engine
• В связи с отсутствием поддержки ODBC драйвера для базы данных проблематично сделать одну унифицированную базу сразу для Android и iOS версии.
• Для поддержки https на iOS требуется приложить некоторые усилия: необходимо вручную добавлять сертификаты от Android сборки.
• При использования сторонних библиотек иногда требуется вносить изменения в настройки proguard, но возможность сделать это стандартным способом через Gradle отсутствует. В результате приходится добавлять нужные флаги вручную.
Сделать это можно в файле proguard.cfg, который находится в /Applications/Intel/INDE/multi_os_engine/tools. Флаги следует просто добавить в конец файла. В нашем случае мы добавили следующие флаги, чтобы использовать Retrofit:
-keepattributes *Annotation* -keep class retrofit.** { *; } -keepclasseswithmembers class * { @retrofit.http.* <methods>; } -keepattributes Signature
• Отсутствует возможность редактировать storyboard непосредственно в Android Studio, приходилось верстать интерфейс в XCode.
Мы радостью продолжим использовать платформы Intel Multi-OS Engine в наших проектах по разработке мобильных решений, поскольку мы рассматриваем этот опыт как новую возможность приобрести уникальную экспертизу и продемонстрировать свое умение справляться со сложными R&D задачами.
ссылка на оригинал статьи http://habrahabr.ru/post/275305/
Добавить комментарий