问题
I have been developing an app in Qt on Ubuntu which works with an SQLite database and several GB of files. Everything is working well in Ubuntu and Windows, but... when I try to port to Android I'm having some problems. I've placed the database.db file and other files on an SD card, installed it in my target device, and finally was able to "find" it with this code:
QString dbName;
#ifdef Q_OS_ANDROID
QAndroidJniObject androidContext = QtAndroid::androidContext();
QAndroidJniObject dir = QAndroidJniObject::fromString(QString(""));
QAndroidJniObject path = androidContext.callObjectMethod("getExternalFilesDir",
"(Ljava/lang/String;)Ljava/io/File;",
dir.object());
// qInfo() << "Path: " + path.toString();
dbName = path.toString()+"/database.db";
#else
QSettings *sp = new QSettings();
dbName = sp->value( "dbName", "/database.db" ).toString();
sp->deleteLater();
#endif
qDebug( "Database::Database(%s)", qPrintable( dbName ) );
const QString DRIVER("QSQLITE");
if ( !QSqlDatabase::isDriverAvailable(DRIVER) )
{ qDebug( "QSqlDatabase::isDriverAvailable(%s) is false", qPrintable(DRIVER) ); }
else
{ db = QSqlDatabase::addDatabase(DRIVER);
db.setDatabaseName( dbName );
QSqlDriver *driver = QSqlDatabase::database().driver();
if ( !db.open() )
{ qDebug( "Error opening database %s: %s", qPrintable( dbName ), qPrintable( db.lastError().text() ) );
}
else
{ qDebug( "Database opened." );
}
}
So, finally by using JNI to track down the SD card folder I'm getting the database to report that it is successfully opened. However, now when I try to read my first table I'm getting failure of the query.exec() in Android (that doesn't happen in Linux/Windows):
QSqlQuery query(db);
query.prepare( "SELECT time FROM history ORDER BY time DESC;" );
if ( !query.exec() ) { qDebug( "ERROR: %s, %s", qPrintable( query.lastError().text() ), qPrintable( query.executedQuery() ) ); return; }
this returns:
D MyApp: ERROR: No query Unable to fetch row, SELECT time FROM history ORDER BY time DESC;
in Android, but works in other OSs. All subsequent attempts to read from the database also fail in Android. MyApp is a console app, I eventually want to run it as a service, but before taking that leap I'd like to see it work in "simple mode." The Application Output does throw a couple of warnings (marked W) before the program starts, but they don't seem connected?
I zygote : Late-enabling -Xcheck:jni
W System : ClassLoader referenced unknown path:
I Qt : qt started
I zygote : Do partial code cache collection, code=26KB, data=23KB
I zygote : After code cache collection, code=26KB, data=23KB
I zygote : Increasing code cache capacity to 128KB
I zygote : Do partial code cache collection, code=62KB, data=55KB
I zygote : After code cache collection, code=62KB, data=55KB
I zygote : Increasing code cache capacity to 256KB
D OpenGLRenderer: HWUI GL Pipeline
I Adreno : QUALCOMM build : 94aeef9, Ie8d0be8cd4
I Adreno : Build Date : 11/09/17
I Adreno : OpenGL ES Shader Compiler Version: EV031.22.00.01
I Adreno : Local Branch :
I Adreno : Remote Branch :
I Adreno : Remote Branch :
I Adreno : Reconstruct Branch :
W RenderThread: type=1400 audit(0.0:2413): avc: denied { search } for name="proc" dev="debugfs" ino=12448 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:qti_debugfs:s0 tclass=dir permissive=0
I Adreno : PFP: 0x005ff087, ME: 0x005ff063
I OpenGLRenderer: Initialized EGL, version 1.4
D OpenGLRenderer: Swap behavior 2
I zygote : Do full code cache collection, code=112KB, data=112KB
I zygote : After code cache collection, code=110KB, data=100KB
D MyApp: Database::Database(/storage/emulated/0/Android/data/com.mangocats.myapp/files/database.db)
D MyApp: Database opened.
D MyApp: ERROR: No query Unable to fetch row, SELECT time FROM history ORDER BY time DESC;
This is with latest stable Android Studio downloaded and configured yesterday, JDK 1.8, latest Qt Creator downloaded and installed yesterday running on Ubuntu 18.04, Qt 5.14.2, targeting an Android 8.0 phone. Sample Qt apps and simple GUI apps I have run on the phone successfully, and the http server side of the console app is working as expected. I've given the app READ and WRITE EXTERNAL STORAGE permissions (even though many notes suggest they are not needed for these versions...)
What else do I have to do for Android to get the SQLite database working?
回答1:
Make sure the file exists
As already mentioned in the comments, before calling
db.setDatabaseName(dbName);
make sure that dbName
refers to an existing file in the filesystem. Because otherwise QSqlDatabase::open()
will silently create an empty SQLite3 database by that name. You would then see an error like yours, or also "Parameter count mismatch" when attempting the first query.
Or prevent open()
from creating the file
If you rather want QSqlDatabase::open()
to fail if the specified database does not exist and only require read-only access, you can utilize a side effect of connection option QSQLITE_OPEN_READONLY
(source):
db.setConnectOptions("QSQLITE_OPEN_READONLY");
db.setDatabaseName(dbName);
if (!db.open()) {
// ...
}
SQLite itself also has an option SQLITE_OPEN_READWRITE that would not restrict this solution to read-only access, but this option is not yet supported in the Qt interface.
And only use ordinary filesystem filenames
For reference, when calling QSqlDatabase::setDatabaseName()
don't try to specify a file using the qrc:/
Qt resource scheme or assets:/
Qt-style Android assets access scheme. The method only accepts plain relative or absolute filesystem filenames (or their equivalent as SQLite-specific file:
URIs). ((This is not mentioned in the documentation, but visible in the Qt source code here: Qt forwards the given filename to sqlite3_open_v2()
and that only accepts filesystem filenames.))
The only way to access a database bundled with the application into the Android APK package is to first copy it to the application's data directory. Because that copy then has has an ordinary filesystem path. of the form /data/data/com.example.appname/files/database.sqlite
.
来源:https://stackoverflow.com/questions/61314573/how-to-read-an-existing-sqlite-database-in-android-using-qt