如何使用 Qt 在 Android 中读取现有的 SQLite 数据库?
Posted
技术标签:
【中文标题】如何使用 Qt 在 Android 中读取现有的 SQLite 数据库?【英文标题】:How to read an existing SQLite database in Android using Qt? 【发布时间】:2020-04-20 03:11:48 【问题描述】:我一直在 Ubuntu 上使用 Qt 开发一个应用程序,它可以与 SQLite 数据库和几 GB 的文件一起使用。在 Ubuntu 和 Windows 中一切都运行良好,但是......当我尝试移植到 android 时,我遇到了一些问题。我已将 database.db 文件和其他文件放在 SD 卡上,将其安装在我的目标设备中,最后能够使用以下代码“找到”它:
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." );
所以,最后通过使用 JNI 来追踪 SD 卡文件夹,我让数据库报告它已成功打开。但是,现在当我尝试读取我的第一个表时,我在 Android 中遇到 query.exec() 失败(这在 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;
返回:
D MyApp: ERROR: No query Unable to fetch row, SELECT time FROM history ORDER BY time DESC;
适用于 Android,但适用于其他操作系统。所有后续从数据库读取的尝试在 Android 中也会失败。 MyApp 是一个控制台应用程序,我最终希望将它作为服务运行,但在实现这一飞跃之前,我希望看到它以“简单模式”运行。应用程序输出确实在程序启动之前抛出了几个警告(标记为 W),但它们似乎没有连接?
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;
这是昨天下载和配置的最新稳定 Android Studio,JDK 1.8,昨天下载和安装的最新 Qt Creator,在 Ubuntu 18.04,Qt 5.14.2 上运行,针对 Android 8.0 手机。我在手机上成功运行的示例 Qt 应用程序和简单的 GUI 应用程序,并且控制台应用程序的 http 服务器端按预期工作。我已授予应用 READ 和 WRITE EXTERNAL STORAGE 权限(尽管许多注释表明这些版本不需要它们......)
我还需要为 Android 做什么才能让 SQLite 数据库正常工作?
【问题讨论】:
检查数据库文件路径是否正确。open()
即使文件不存在也不会返回 false;它将创建一个新的空数据库。
好消息,我敢打赌就是这样。
【参考方案1】:
确保文件存在
正如在 cmets 中已经提到的,在调用之前
db.setDatabaseName(dbName);
确保dbName
引用文件系统中的现有文件。因为否则QSqlDatabase::open()
将通过该名称默默地创建一个空的 SQLite3 数据库。然后,您会在尝试第一次查询时看到类似您的错误,或者“参数计数不匹配”。
或者阻止open()
创建文件
如果您希望QSqlDatabase::open()
在指定数据库不存在且只需要只读访问的情况下失败,您可以利用连接选项QSQLITE_OPEN_READONLY
(source) 的副作用:
db.setConnectOptions("QSQLITE_OPEN_READONLY");
db.setDatabaseName(dbName);
if (!db.open())
// ...
SQLite 本身也有一个option SQLITE_OPEN_READWRITE
,它不会将此解决方案限制为只读访问,但 Qt 界面尚不支持此选项。
并且只使用普通的文件系统文件名
作为参考,在调用QSqlDatabase::setDatabaseName()
时不要尝试使用qrc:/
Qt 资源方案或assets:/
Qt 样式的Android 资产访问方案来指定文件。该方法只接受简单的相对或绝对文件系统文件名(或它们与 SQLite 特定的 file:
URI 等效的文件名)。 ((这在文档中没有提到,但在 Qt 源代码here 中可见:Qt 将给定的文件名转发到sqlite3_open_v2()
和that 只接受文件系统文件名。))
访问与应用程序捆绑到 Android APK 包中的数据库的唯一方法是首先将其复制到应用程序的数据目录。因为那个副本有一个普通的文件系统路径。形式为/data/data/com.example.appname/files/database.sqlite
。
【讨论】:
以上是关于如何使用 Qt 在 Android 中读取现有的 SQLite 数据库?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 android 应用程序中使用现有的 Sip Stack 库?
如何在 Android 平板电脑上实现主从视图 Qt/QML?
如何在 WP7 的 WebBrowser 控件中读取现有的 html 文件?