The SubstrateVM based backend does not come with Android APIs, and those APIs will take a lot of effort to port to the new VM. Please vote accordingly and carefully:
My app does not use any Android APIs
My app uses some Android APIs and can be updated to an alternative
My app relies on Android APIs and is impossible to switch to an alternative
0voters
And if you need those Android APIs, please comment below about which API you are using so I could put effort on things that really matters.
Just a quick note: most of the offered Android APIs are there “by accident”: because they were used / required the Android implementation of the Java standard library.
I think it would be safe to assume that these APIs will not be supported going forward (especially with the SubstrateVM backend). If a specific Android API is useful for many users, then a now moe-android module could be provided with these APIs (like e.g. android.database)
Yes, not so hard. I created ORM library that have the same interface in Android and iOS. On iOS implementation it use org.sqlite.c. I think I can share everything as a library on GitHub if need.
Here is some usefull POC of using pure sqlite.
package com.stayfit.queryorm.lib.ios;
import com.stayfit.queryorm.lib.TypeConverter;
import org.moe.natj.general.ptr.Ptr;
import org.moe.natj.general.ptr.VoidPtr;
import org.moe.natj.general.ptr.impl.PtrFactory;
import org.sqlite.c.Globals;
import java.util.Date;
public class SQLiteStatement {
private final int SQL_OK = 0;
private final int SQL_ROW = 100;
private final int SQL_DONE = 101;
private final String statement;
private final Object[] bindArgs;
private VoidPtr stmtHandle;
private VoidPtr dbHandle;
private String lastError;
private int affectedCount = 0;
private long lastInsertedID = -1;
VoidPtr getStmtHandle() {
return stmtHandle;
}
VoidPtr getDbHandle() {
return dbHandle;
}
String getLastError() {
return lastError;
}
public SQLiteStatement(String statement, Object[] bindArgs) {
if (statement == null) {
throw new NullPointerException();
}
this.statement = statement;
this.bindArgs = bindArgs == null ? new Object[0] : bindArgs;
}
public boolean prepare(VoidPtr dbHandle) {
if (dbHandle == null) {
throw new NullPointerException();
}
this.dbHandle = dbHandle;
@SuppressWarnings("unchecked") Ptr<VoidPtr> stmtRef = (Ptr<VoidPtr>) PtrFactory
.newPointerPtr(Void.class, 2, 1, true, false);
int err = Globals.sqlite3_prepare_v2(dbHandle, statement, -1, stmtRef, null);
if (err != 0) {
lastError = Globals.sqlite3_errmsg(dbHandle);
return false;
}
stmtHandle = stmtRef.get();
int idx = 0;
for (Object bind : bindArgs) {
idx++;
if (bind instanceof String) {
err = Globals.sqlite3_bind_text(stmtHandle, idx, (String) bind, -1, null);
} else if (bind instanceof Integer) {
err = Globals.sqlite3_bind_int(stmtHandle, idx, (Integer) bind);
} else if (bind instanceof Long) {
err = Globals.sqlite3_bind_int64(stmtHandle, idx, (Long) bind);
} else if (bind instanceof Double) {
err = Globals.sqlite3_bind_double(stmtHandle, idx, (Double) bind);
} else if (bind instanceof Float) {
err = Globals.sqlite3_bind_double(stmtHandle, idx, (Float) bind);
} else if (bind instanceof Boolean) {
err = Globals.sqlite3_bind_int(stmtHandle, idx, ((Boolean) bind) ? 1 : 0);
} else if (bind instanceof Date) {
String value = new TypeConverter().writeDateTime((Date)bind);
err = Globals.sqlite3_bind_text(stmtHandle, idx, value, -1, null);
} else if (bind == null) {
err = Globals.sqlite3_bind_null(stmtHandle, idx);
} else {
lastError = "No implemented SQLite3 bind function found for " + bind.getClass()
.getName();
return false;
}
if (err != 0) {
lastError = Globals.sqlite3_errmsg(dbHandle);
return false;
}
}
return true;
}
public boolean exec() {
if (stmtHandle == null) {
throw new RuntimeException("statement handle is closed");
}
int err = Globals.sqlite3_step(stmtHandle);
if (err == SQL_ROW || err == SQL_DONE) {
affectedCount = Globals.sqlite3_changes(dbHandle);
lastInsertedID = Globals.sqlite3_last_insert_rowid(dbHandle);
}
if (err != SQL_DONE && err != SQL_ROW) {
lastError = Globals.sqlite3_errmsg(dbHandle);
close();
return false;
}
close();
return true;
}
public SQLiteCursor query() {
return new SQLiteCursor(this);
}
void close() {
if (stmtHandle != null) {
Globals.sqlite3_finalize(stmtHandle);
stmtHandle = null;
dbHandle = null;
}
}
boolean step() {
if (stmtHandle == null) {
throw new RuntimeException("statement handle is closed");
}
int err = Globals.sqlite3_step(stmtHandle);
if (err != SQL_ROW) {
lastError = Globals.sqlite3_errmsg(dbHandle);
return false;
}
return true;
}
boolean reset() {
if (stmtHandle == null) {
throw new RuntimeException("statement handle is closed");
}
return Globals.sqlite3_reset(stmtHandle) == SQL_OK;
}
public String getStatement() {
return statement;
}
public int getAffectedCount() {
return affectedCount;
}
public long getLastInsertedID() {
return lastInsertedID;
}
}