📜 ⬆️ ⬇️

Experience using the Intel Multi-OS Engine to develop an iOS-based Java application

In August, at the Intel Developer Forum in San Francisco, we presented a native mobile patient monitoring application for iPads, developed using the Intel Multi-OS Engine . The application provides data on the most important parameters of the patient's condition by connecting to the bedside monitors via a WiFi network (for more information about the application itself and its functionality, you can read on our website ).
In this article we will share the experience of using the Intel Multi-OS Engine platform, which allows you to develop native iOS applications in Java.

In the case when you need to develop applications for both Android and iOS, the ability to use Java to develop applications for iOS saves time and resources for development.

Key Benefits of Working with the Intel Multi-OS Engine


UIElements


Based on experience, we can say that the main positive feature of the Multi OS-Engine platform is the ability to work with native iOS UI elements, in much the same way as in XCode. All the elements necessary for developing the application were already in the platform, and, accordingly, it was not necessary to add something extra. Each element is fully described: all characteristics and methods are present, which makes it easier for developers who have experience with iOS to work with the platform.
Proper organization of the application
Due to the fact that this platform will further allow developing applications for Android and iOS right away, it is necessary to take a very careful approach to developing the application architecture, properly separating general and UI specific functionalities. It allows you to build very accurate and lightweight applications and organizes the developer.

Java features


The advantage of this platform is that it allows you to implement most of the features of Java. For example, consider working with streams and files:
')
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(); } } 

In the process of work, we conducted additional research on working with the network and found that the Retrofit framework works well with okHttp.

Conveniently and quickly write iOS specific part in Java


Additionally, you can use ObjC-approach to run in the background. In our case, when starting the application, we started the process of reading files:

 @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(); } 

Running separate functions in the main thread is also not a problem:

 public void heartRate(PatientRealData data) { performSelectorOnMainThreadWithObjectWaitUntilDone(new SEL("updatePatientData:"), } @Selector("updatePatientData:") @Generated public void updatePatientData(PatientRealData data) { mHrLabel.setText(String.valueOf(data.getHeartRate())); } 

Access to resources is similar with the iOS API: to get the image “alarm_on.png” from resources and assign it to a button, you just need to do the following:

 UIImage image = UIImage.imageNamed("alarm_on"); mAlarmButton.setImageForState(image, UIControlState.Normal); 

It is very convenient that the syntax of working with the iOS API in Java practically does not differ from the original in ObjC. For example, a menu based on UITableViewController was added this way:

 @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 for Android Studio


A major advantage of the platform from Intel is the "depth" of the integration of the Multi-OS Engine plugin in Android Studio. You can almost all the development carried out in Android Studio. At the same time, you can easily configure the project to work with two platforms at once. To do this, it is enough to configure the configurations to launch the Android and iOS versions and switch between them, simply by selecting the one you need:



Connecting graphs


To implement the display of "running" graphs, the OpenGL experience written in C was used. In order to use them, the UIWaveFormVC controller was written on ObjC, where the C code was already added. This controller on the input data draws on the OpenGL View corresponding points with a given speed and color.

 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 { //     } 

Next, to use this class inside the Multi OS-Engine, we need to generate a “wrapper” for it, that is, implement Java binding to 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(); } } 

Next we simply add the UIWaveFormVC to the logic of the screens in the MainUI.storyboard .

Then all the work is done directly from Java. To transfer data to our UIWaveFormVC, we declare the prepareForSequeSender () method, which allows us to obtain an instance of the controller class before displaying it and transmit data to it.

 @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; } } 

Recommendations for improving the Intel Multi-OS Engine


• Due to the lack of support for ODBC drivers for the database, it is problematic to make one unified database for Android and iOS versions at once.
• To support https on iOS, some effort is required: you must manually add certificates from the Android assembly.
• When using third-party libraries, it is sometimes necessary to make changes to the proguard settings, but there is no way to do this in the standard way via Gradle. As a result, you have to add the necessary flags manually.
This can be done in the file proguard.cfg, which is located in / Applications / Intel / INDE / multi_os_engine / tools . Flags should simply be added to the end of the file. In our case, we added the following flags to use Retrofit:

 -keepattributes *Annotation* -keep class retrofit.** { *; } -keepclasseswithmembers class * { @retrofit.http.* <methods>; } -keepattributes Signature 

• There is no possibility to edit the storyboard directly in Android Studio, you had to type out the interface in XCode.

We are pleased to continue to use the Intel Multi-OS Engine platform in our mobile solutions development projects, as we see this experience as a new opportunity to acquire unique expertise and demonstrate our ability to cope with complex R & D tasks.

Source: https://habr.com/ru/post/275305/


All Articles