我的应用程序是一个医疗数据查看器,患者佩戴的传感器通过蓝牙低能量传输数据。该应用程序是在Objective C中开发的,面向iOS平台。现在应用程序需要移植到Android平台。
iOS的当前设计和实现如下:
由于通信特定于Core Bluetooth API,因此必须为Android重新编写。 ui层应该很容易随便移动,因为它完全取决于Phonegap。然而,对于持久性和逻辑层,我正在寻找一种方法来将它们自动转换为Android,或者以可重用于两个平台的方式重新编写它们。
一种方法可以是将相关代码从Objective C自动转换为Java。非UI类有converter to go from Java to objective C个。
iOS / Objective C到Android / Java有类似的转换器吗? 是否有其他方法或框架可以使用?
答案 0 :(得分:7)
似乎有: http://code.google.com/p/objc2j/
可以通过http://objc2j.googlecode.com/svn/
访问存储库我自己没有检查过,所以请发表您的意见。
答案 1 :(得分:5)
Google有一些开源项目可以做到这一点。
您需要使用SVN来访问这些存储库。以下是链接:
Java to Objective C:http://code.google.com/p/j2objc/
目标C到Java:http://code.google.com/p/objc2j/
祝你好运!答案 2 :(得分:2)
最好的办法是使用Apportable。它是一个提供clang端口,objective-c运行时以及iOS上大多数框架(包括UIKit)的平台。
还没有Core Bluetooth包装器,但您可以从他们的平台调用java API。 FMDatabase可以正常工作,理论上电话间隙界面应该可以正常工作。
我会避免代码生成器的建议。如果你有一种显着的代码库,他们最终会花很多时间重新实现你已经构建的所有内容。
答案 3 :(得分:0)
我已经将O2J - Objective-C to Java Converter用于类似的场景,但效果非常好。
如果没有太多工作,它将在你的算法/逻辑上做得很好。
可自定义,因此您可以为自己的蓝牙代码添加自己的翻译。如果API的工作方式相同,你可能可以直接将蓝牙方法调用转换为java,但它们可能不会。最好在您的Objective-C代码中为蓝牙设置一层间接,以便真正轻松提供Android特定的实现。例如,创建一个BluetoothHelper.m和一个BluetoothHelper.java,翻译将更加顺畅。
我已将它用于使用FMDatabase的项目。对于FMDatabase部分,我们已经将FMDatabase / FMResultSet作为间接层!我自己实现了FMDatabase / FMResultSet,因为sqlite Objective-c(基于c的sqlite函数)的API与Android太不同了。 O2J帮助我开始翻译FMDatabase / FMResultSet,这就是我最终得到的......
FMDatabase:
out.flush();
FMResultSet:
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
}
}