Porting iOS Objective-c to Android Java

My application is a medical data viewer in which patients wear a sensor that transmits low-energy Bluetooth data. The app was developed in Objective-C, focused on the iOS platform. Now the application needs to be ported to the Android platform.

The current design and implementation for iOS is as follows:

  • Connectivity - Target C specific to Core API Bluetooth
  • data / persistence - Objective C using FMDatabase as an interface for SQLite
  • Algorithms / Logic - Objective C
  • ui - Phonegap-based JavaScript / HTML5

Since the connection is specific to the Core Bluetooth API, it will need to be rewritten for Android. The ui level should be easily portable without much change, since it is completely shareable on Phonegap. However, for perseverance and logic levels, I'm looking for a way to automatically convert them to Android or rewrite them so that they are reused for both platforms.

One approach might be to automatically convert the corresponding code from Objective-C to Java. There is a converter for moving from Java to a C object for classes other than the UI.

Is there a similar converter for iOS / C lens on Android / Java? Are there other approaches or frameworks that can be used?

+6
source share
4 answers

It looks like there is: http://code.google.com/p/objc2j/

The repository must be accessible through http://objc2j.googlecode.com/svn/

I didn’t check it myself, so please write your opinion about it.

+7
source

Google has open source projects that do this.

You will need to use SVN to access these repositories. Here are the links:

Java for Objective C: http://code.google.com/p/j2objc/

C lens in Java: http://code.google.com/p/objc2j/

Good luck

+5
source

Best to use Apportable . It is a platform that provides the clang port, objective-c runtime, and most iOS frameworks (including UIKit).

There is no Core Bluetooth chip yet, but for this you can call Java APIs from your platform. FMDatabase will work fine, and the phone break interface should theoretically work fine.

I would avoid suggestions from code generators. They end up eating a lot of time, redefining everything that you have already created, if you have a type of meaningful code base.

+2
source

I used O2J - Objective-C to Java Converter for a similar scenario, and it worked very well.

It will do great with your algorithms / logic without much effort.

It is customizable, so you can add your own translations for your Bluetooth code. You can get by translating the bluetooth method calls directly to java if the APIs work the same but probably not. It’s best to have an indirect layer in the Objective-C code for bluetooth to make it really easy to implement a specific Android version. For example, create BluetoothHelper.m and BluetoothHelper.java, and the translation will be much smoother.

I used it for projects that used FMDatabase. For the FMDatabase part, we already have FMDatabase / FMResultSet as an indirect layer! I myself implemented FMDatabase / FMResultSet, since the API for sqlite Objective-C (based on sqlite functions) is too different from Android. O2J helped me start translating FMDatabase / FMResultSet, and this is what I ended up with ...

FMDatabase:

public class FMDatabase { private SQLiteDatabase database; private HashMap<String, SQLiteStatement> compiled; public FMDatabase(SQLiteDatabase database) { this.database = database; } public FMResultSet executeQuery_arguments(String sql, Object... args) { synchronized (database) { String[] selectionArgs = objectArgsAsStrings(args); Cursor rawQuery = database.rawQuery(sql, selectionArgs); return new FMResultSet(rawQuery); } } public FMResultSet executeQuery(String sql, Object... args) { synchronized (database) { String[] selectionArgs = objectArgsAsStrings(args); Cursor rawQuery = database.rawQuery(sql, selectionArgs); return new FMResultSet(rawQuery); } } public String debugQuery(String sql, Object...args) { StringBuilder sb = new StringBuilder(); FMResultSet rs = executeQuery(sql, args); rs.setupColumnNames(); HashMap names = rs.columnNameToIndexMap(); Set ks = names.keySet(); for (Object k : ks) { sb.append(k); sb.append("\t"); } sb.append("\n"); while(rs.next()) { for (Object k : ks) { String key = k.toString(); if(rs.getType(key) == Cursor.FIELD_TYPE_STRING) { sb.append(rs.stringForColumn(key)); } else if(rs.getType(key) == Cursor.FIELD_TYPE_INTEGER) { sb.append(rs.longForColumn(key)); } else if(rs.getType(key) == Cursor.FIELD_TYPE_FLOAT) { sb.append(rs.doubleForColumn(key)); } else if(rs.getType(key) == Cursor.FIELD_TYPE_BLOB) { sb.append(rs.stringForColumn(key)); } else { sb.append("<NOT STRING>"); } sb.append("\t"); } sb.append("\n"); } return sb.toString(); } public String[] objectArgsAsStrings(Object... args) { String[] selectionArgs = new String[args.length]; for (int i = 0; i < args.length; i++) { Object o = args[i]; if(o instanceof Date) { selectionArgs[i] = Long.toString(((Date) o).getTime()); } else if(o instanceof Boolean) { selectionArgs[i] = ((Boolean) o).booleanValue() ? "TRUE" : "FALSE"; } else { selectionArgs[i] = args[i] == null ? "" : o.toString(); } } return selectionArgs; } public boolean executeUpdate_arguments(String sql, Object... args) { synchronized (database) { String[] selectionArgs = objectArgsAsStrings(args); database.execSQL(sql, selectionArgs); return true; } } public boolean executeUpdate(String sql, Object... args) { synchronized (database) { SQLiteStatement statement = bindToCachedCompiledStatement(sql, args); statement.execute(); return true; } } private SQLiteStatement bindToCachedCompiledStatement(String sql, Object... args) { HashMap<String, SQLiteStatement> statments = getCompiledStatements(); SQLiteStatement statement = statments.get(sql); if (statement == null) { statement = database.compileStatement(sql); statments.put(sql, statement); } statement.clearBindings(); // bindAllArgsAsStrings(statement, objectArgsAsStrings(args)); bindAllArgs(statement, args); return statement; } private void bindAllArgs(SQLiteStatement statement, Object[] bindArgs) { if (bindArgs == null) { return; } int size = bindArgs.length; for (int i = 0; i < size; i++) { Object arg = bindArgs[i]; int index = i + 1; if(arg == null) { statement.bindNull(index); } else if (arg instanceof String) { statement.bindString(index, (String) arg); } else if (arg instanceof Double || arg instanceof Float) { Number numArg = (Number) arg; statement.bindDouble(index, numArg.doubleValue()); } else if (arg instanceof Integer || arg instanceof Long) { Number numArg = (Number) arg; statement.bindDouble(index, numArg.longValue()); } else { statement.bindString(index, arg.toString()); } } } public long executeInsert(String string, Object... args) { synchronized (database) { SQLiteStatement statement = bindToCachedCompiledStatement(string, args); try { return statement.executeInsert(); } catch (Exception e) { Log.i("STD", "No Rows inserted", e); return 0; } } } public void bindAllArgsAsStrings(SQLiteStatement statement, String[] bindArgs) { if (bindArgs == null) { return; } int size = bindArgs.length; for (int i = 0; i < size; i++) { statement.bindString(i + 1, bindArgs[i]); } } private HashMap<String, SQLiteStatement> getCompiledStatements() { if (compiled == null) { compiled = new HashMap<String, SQLiteStatement>(); } return compiled; } public boolean rollback() { synchronized (database) { database.execSQL("ROLLBACK;"); } return true; } public boolean commit() { synchronized (database) { database.execSQL("COMMIT;"); } return true; } public boolean beginDeferredTransaction() { synchronized (database) { database.execSQL("BEGIN DEFERRED TRANSACTION;"); } return true; } public boolean beginTransaction() { synchronized (database) { database.execSQL("BEGIN EXCLUSIVE TRANSACTION;"); } return true; } public boolean open() { return true; } public void setShouldCacheStatements(boolean shouldCacheStatements) { // TODO } } 

FMResultSet:

 public class FMResultSet { private boolean columnNamesSetup; private HashMap<String, Number> columnNameToIndexMap; private Cursor rawQuery; public FMResultSet(Cursor rawQuery) { this.rawQuery = rawQuery; } public void close() { rawQuery.close(); } public void setupColumnNames() { if (columnNameToIndexMap == null) { this.setColumnNameToIndexMap(new HashMap()); } int columnCount = rawQuery.getColumnCount(); int columnIdx = 0; for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { columnNameToIndexMap.put(rawQuery.getColumnName(columnIdx).toLowerCase(), new Integer(columnIdx)); } columnNamesSetup = true; } public boolean next() { return rawQuery.moveToNext(); } public int columnIndexForName(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } columnName = columnName.toLowerCase(); Number n = columnNameToIndexMap.get(columnName); if (n != null) { return NumberValueUtil.intVal(n); } Log.i("StdLog", String.format("Warning: I could not find the column named '%s'.", columnName)); return -1; } public int intForColumn(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } int columnIdx = this.columnIndexForName(columnName); if (columnIdx == -1) { return 0; } return intForColumnIndex(columnIdx); } public int intForColumnIndex(int columnIdx) { return rawQuery.getInt(columnIdx); } public long longForColumn(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } int columnIdx = this.columnIndexForName(columnName); if (columnIdx == -1) { return 0; } return longForColumnIndex(columnIdx); } public long longForColumnIndex(int columnIdx) { return (long) rawQuery.getLong(columnIdx); } public boolean boolForColumn(String columnName) { return (this.intForColumn(columnName) != 0); } public boolean boolForColumnIndex(int columnIdx) { return (this.intForColumnIndex(columnIdx) != 0); } public double doubleForColumn(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } int columnIdx = this.columnIndexForName(columnName); if (columnIdx == -1) { return 0; } return doubleForColumnIndex(columnIdx); } public double doubleForColumnIndex(int columnIdx) { return rawQuery.getDouble(columnIdx); } public String stringForColumnIndex(int columnIdx) { return rawQuery.getString(columnIdx); } public String stringForColumn(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } int columnIdx = this.columnIndexForName(columnName); if (columnIdx == -1) { return null; } return this.stringForColumnIndex(columnIdx); } public Date dateForColumn(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } int columnIdx = this.columnIndexForName(columnName); if (columnIdx == -1) { return null; } return new Date((this.longForColumn(columnName))); } public Date dateForColumnIndex(int columnIdx) { return new Date((this.longForColumnIndex(columnIdx))); } public byte[] dataForColumn(String columnName) { if (!columnNamesSetup) { this.setupColumnNames(); } int columnIdx = this.columnIndexForName(columnName); if (columnIdx == -1) { return null; } return this.dataForColumnIndex(columnIdx); } public byte[] dataForColumnIndex(int columnIdx) { return rawQuery.getBlob(columnIdx); } public HashMap columnNameToIndexMap() { return columnNameToIndexMap; } public void setColumnNameToIndexMap(HashMap value) { columnNameToIndexMap = value; } @SuppressLint("NewApi") public int getType(String string) { return rawQuery.getType(columnIndexForName(string)); } } 
0
source

All Articles