Home Features Docs Blog Support GitHub

Poll: Whether to keep Android APIs

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

0 voters

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 the main consideration is the database api as my own app uses that :wink: But it shouldn’t be hard to replace it with the pure sqlite java library.

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

I use next android libs:
android.util.Log;
android.util.Base64;
android.os.SystemClock;
android.util.ArrayMap;
android.util.SparseBooleanArray;

I’d love to look at your ORM implementation. Are you using some existing sqlite cocoapod?

The new SVM based MOE will come with javesqlite built in (that’s the current plan). Also iOS SDK has built in sqlite 3 support and you can generate java bindings to those APIs, see https://doc.multi-os-engine.org/multi-os-engine/7_creating_db_app/2_part2/part2.html and https://github.com/multi-os-engine/moe-samples-java/tree/moe-master/MuseumMap/ios/src/main/java/org/sqlite

I think these can be replaced relatively easily.

Thank you for reminder about those. I’ve read those tutorials while back, but completely forgot about all that stuff.