Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt C++ library in Android Eclipse project: QSQLITE driver not loaded

I've created a Qt dynamic lib that uses Qt SQL to open an SQLite database, but I'm getting this error:

QSqlDatabase: QSQLITE driver not loaded
QSqlDatabase: available drivers: 

The DLL was working fine as part of a Qt Android application, however I need to use it through JNI from an existing Java application developed in Eclipse.

This is the shortest example code that reproduces the problem. I load the library from Java and call its init() method:

System.loadLibrary("plugins_sqldrivers_libqsqlite");
System.loadLibrary("Qt5Sql");
System.loadLibrary("MyQtLib");
MyQtLib.init();

And inside the Qt library I just call QSqlDatabase::addDatabase():

JNIEXPORT void JNICALL Java_test_MyQtLib_foo(JNIEnv *, jclass)
{
    // Manually create a QCoreApplication instance.
    int argc = 1;
    static char arg[] = "";
    static char *arg2 = arg;
    app = new QCoreApplication(argc, &arg2);
    // Try to add an SQLite db connection.
    QSqlDatabase::addDatabase("QSQLITE");
}

Since the error is QSQLITE driver not loaded, and the Qt library was working inside a Qt application, I assume that Qt is doing some initialization that I'm missing.

But this didn't remove the error, so it must be something else. Normally, the Qt application will use QtApplication.java and QtActivity.java to perform some initialization, so they must be doing something more there that I'm not doing.

like image 739
sashoalm Avatar asked Jul 24 '15 09:07

sashoalm


1 Answers

Eventually I stepped into the Qt sources to see how the SQLITE plugin is loaded (for the desktop build at least).

The relevant function was QFactoryLoader::update(). In it I noticed that it iterates all the directories in QCoreApplication::libraryPaths():

QStringList paths = QCoreApplication::libraryPaths();
for (int i = 0; i < paths.count(); ++i) {

If any of them has a sub-directory named "sqldrivers", it goes inside it and tries to load all the dynamic libraries in that sub-directory.

I then printed out the library paths in a test project I ran directly from Qt Creator - qDebug() << a.libraryPaths();, and I saw this path - /data/data/org.qtproject.example.untitled/qt-reserved-files/plugins. In this directory on my android phone there was a subdirectory named sqldrivers, that contained a single file - libqsqlite.so.

I then checked the .java files, and indeed QtActivity::startApp() adds the library path:

boolean bundlingQtLibs = false;
if (m_activityInfo.metaData.containsKey("android.app.bundle_local_qt_libs")
    && m_activityInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) {
    localPrefix = getApplicationInfo().dataDir + "/";
    pluginsPrefix = localPrefix + "qt-reserved-files/";
    cleanOldCacheIfNecessary(localPrefix, pluginsPrefix);
    extractBundledPluginsAndImports(pluginsPrefix);
    bundlingQtLibs = true;
}

The solution then would be to ensure that there is a sqldrivers/libqsqlite.so somewhere on the phone, and then add the parent folder of sqldrivers to the library path using QCoreApplication::addLibraryPath().

like image 157
sashoalm Avatar answered Sep 21 '22 13:09

sashoalm