首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
Qt 5.12.1 Android 连接 MySQL:交叉编译驱动与 APK 打包全流程
📅 2026/10/22 21:21:41
✍️ 爱科研究院
👁 阅读 3,247
简介面向需要在Qt 5.12.1 Android应用中接入MySQL的开发者这份资源围绕交叉编译MySQL驱动、生成Qt SQL插件、打包APK到调用QSqlDatabase建立连接的完整链路整理提供了一套可复用的配置参考。内容侧重Ubuntu与Windows双平台下的依赖安装、NDK工具链设置及架构选择针对Android默认不含QMYSQL驱动这一常见痛点给出了分步骤的落地思路。压缩包共934个文件以h头文件、c源码、txt说明文档、m4配置宏及po翻译文件为主整体仅16.51MB便于快速下载其中还包含mysql、iconv等底层库源码与编译辅助文件可结合项目实际对照检查版本兼容和链接路径问题。当前已有777人学习下载适合有一定Qt Android基础、希望自行定制数据库驱动的中高级开发者。按此资料推进能在很大程度上减少驱动编译、插件放置和APK打包环节的试错时间。1. Qt 5.12.1 在 Android 上接 MySQL先把驱动这件事想清楚Qt 5.12.1 的 Android 工程要连 MySQL真正卡人的不是 SQL 查询怎么写而是 Qt 默认发布包里压根没有 Android 版的 QMYSQL 驱动。你拿桌面版 Qt 编好的 libqsqlmysql.so 直接塞进 APK装到手机上会得到一行 QSqlDatabase: QMYSQL driver not loaded。原因不复杂驱动本质是个插件插件连同它依赖的 libmysqlclient.so都得针对 Android 的 ABI 单独编译、单独打包。这套流程适合手里已有现成 MySQL 服务器、准备在 Ubuntu 或 Windows 上搭环境给安卓应用做数据落地的开发者也需要一点交叉编译的耐心。这篇笔记把依赖、驱动、APK 打包、真机连接和常见翻车点串成一条可复现的链路。2. 交叉编译依赖MySQL 客户端库按架构编译的三个关键参数Windows 和 Ubuntu 两条路线第一步都逃不开交叉编译四个字。Android 设备上是 ARM 架构开发机上装的是 x86_64 的 libmysqlclient直接把 .so 拷进去等于把桌面的东西塞进手机运行时会因为 ABI 对不上直接退出。这一步要把 MySQL 的 C 客户端库用 Android NDK 的 clang 工具链重新编一份也是整个流程里最像黑匣子的一段。编译前先把三个参数定死目标 ABI、API 级别、安装目录。ABI 错了 APK 装上去跑不起来API 级别低了链接器会报一堆符号缺失安装目录不统一后面编 Qt 插件时还得回头找库。2.1 依赖选型libmysqlclient 与 mariadb-connector-c 的取舍在 Ubuntu 上 apt 安装 libmysqlclient-dev 只能满足桌面端原型验证Android 交叉编译必须走源码路线。常见的两个选择是官方 mysql-connector-c 和 mariadb-connector-c两者协议兼容 MySQL 服务端但源码依赖和后续维护手感差别不小。官方 mysql-connector-c 6.1.x 胜在稳妥CMake 构建链成熟老项目参考资料多mariadb-connector-c 胜在依赖少、社区提交活跃对 Android sysroot 的适配更省心。如果目标服务端是 MySQL 8还要留意认证插件问题mariadb-connector-c 对 caching_sha2_password 的兼容性通常更好。客户端库优点注意事项官方 mysql-connector-c 6.1.x与 MySQL 官方行为一致文档多与 MySQL 8 新认证插件需额外处理mariadb-connector-c编译依赖少社区活跃部分旧接口命名与官方略有差异系统 libmysqlclient-dev桌面调试方便不能用于 Android ABI我一般建议用 mariadb-connector-c 做 Android 交叉编译少踩两个坑。第一步先把 NDK 解压到固定路径后面所有脚本都引用同一个 NDK 路径避免多 NDK 版本混用导致工具链对不上。2.2 NDK 环境变量与交叉编译命令NDK 选 r19里面自带了 android.toolchain.cmake不需要手动去配 CC、CXX、AR 那一串环境变量CMake 会按 ABI 自动切换对应 clang 前缀。下面这段命令针对 arm64-v8a如果目标机器是老的 32 位 ARM把 CMAKE_TOOLCHAIN_FILE 之外的 ANDROID_ABI 换成 armeabi-v7a 再编一遍即可。INSTALL_DIR 指向一个独立目录编 Qt 插件时全靠这个路径找头文件和库文件。export NDK/opt/android-ndk-r19c export API21 export ABIarm64-v8a export INSTALL_DIR/opt/mysql-android-arm64 cmake -S . -B build \ -DCMAKE_TOOLCHAIN_FILE$NDK/build/cmake/android.toolchain.cmake \ -DANDROID_ABI$ABI \ -DANDROID_PLATFORMandroid-$API \ -DCMAKE_INSTALL_PREFIX$INSTALL_DIR \ -DWITH_SSLbundled \ -DWITH_UNIT_TESTSOFF cmake --build build -j$(nproc) cmake --install build-DANDROID_PLATFORM 对应 API 21 表示 Android 5.0覆盖绝大多数存量设备。WITH_SSLbundled 会把 SSL 支持一起编进客户端库后续如果走 TLS 连接不用回头补依赖。WITH_UNIT_TESTSOFF 是为了缩短编译时间Android 交叉编译本来就慢测试代码在这里没有价值。整个命令执行完在 /opt/mysql-android-arm64/lib 下会生成 libmysqlclient.so 以及在 include 目录放好头文件。提示如果用了 NDK r17 及更早版本没有现成 toolchain file得手动指定 CC 和 CXX 指向 clang 或 gcc 包装脚本建议直接升级 NDK别在旧工具链上浪费时间。2.3 产物检查file、readelf 确认架构不是玄学交叉编译最怕的就是编完了不知道编出来什么架构。libmysqlclient.so 生成后先跑 file 和 readelf 两个命令确认再往下走。这一步只需要十秒钟能避免后面把错误架构的库打进 APK、运行时报一堆莫名其妙的 dlopen failed。file /opt/mysql-android-arm64/lib/libmysqlclient.so readelf -d /opt/mysql-android-arm64/lib/libmysqlclient.so | grep SONAMEfile 输出里会明确显示 ARM aarch64如果看到 x86-64 说明编译时 CMake 没有走 Android 工具链回 2.2 检查 NDK 路径和 ANDROID_ABI。readelf 的 SONAME 这一项要记下来它决定 Android 系统加载库时按什么文件名去找。如果 SONAME 显示为 libmysqlclient.so.18而 APK 里只拷贝了 libmysqlclient.so真机运行照样报找不到库这个细节后面避坑章节还会再遇到。3. Qt SQL 驱动插件qsqlmysql 怎么编译进 APKMySQL 客户端库只是底层依赖Qt 侧还差一个驱动插件 libqsqlmysql.so。这个插件负责把 Qt 的 QSqlDatabase 请求转成 MySQL C API 调用。大多数人在这一步翻车是因为不知道插件要单独编译以为安装 Qt 时已经自带了。Qt for Android 的自带 SQL 驱动只有 QSQLITEQMYSQL 属于用户自己编译的范畴。安卓的原生库打包也和桌面不一样最终 APK 里必须同时存在插件本身和它链接的 MySQL 客户端库缺一个都会在运行时出问题。3.1 从 Qt 源码里取出 qsqlmysql 插件目录Qt 5.12.1 的驱动插件源码在 qtbase/src/plugins/sqldrivers/qsql_mysql同一个父目录下还有 qsqldriverbase.pri 这类公共配置文件。不要直接在 Qt 安装目录里修改把整个 sqldrivers 目录复制到项目外的工作目录后面编乱了随时删掉重来。复制范围里保留 qsql_mysql.pro、qsql_mysql.pri、qsqldriverbase.pri插件编译时这三个文件缺一不可。qsql_mysql.pro 是入口qsql_mysql.pri 里定义了去哪里找 MySQL 头文件和库qsqldriverbase.pri 提供了驱动插件的公共编译参数。拿到源码后先打开 qsql_mysql.pri 看一遍里面有两个关键变量QMAKE_INCDIR_MYSQL 和 QMAKE_LIBDIR_MYSQL后面编译前就得把这两个变量指到 2.2 节的安装目录。3.2 用 Android qmake 编译插件Qt 5.12.1 安装时如果勾选了 Android 组件会生成对应 ABI 的 qmake比如 android_arm64_v8a/bin/qmake。编译插件时要用这个 qmake而不是桌面版的。桌面版 qmake 编出来的插件链接的是 x86_64 的库装到手机上必然驱动加载失败。命令里把两个环境变量指到交叉编译的 MySQL 目录qmake 会通过 qsql_mysql.pri 读取它们。export ANDROID_QMAKE/opt/Qt/5.12.1/android_arm64_v8a/bin/qmake export QMAKE_INCDIR_MYSQL/opt/mysql-android-arm64/include export QMAKE_LIBDIR_MYSQL/opt/mysql-android-arm64/lib cd work/sqldrivers/qsql_mysql $ANDROID_QMAKE qsql_mysql.pro make -j$(nproc)编译完成后当前目录下会生成 libqsqlmysql.so。这里有几个参数要盯紧ANDROID_QMAKE 必须与之前编译 MySQL 库的 ABI 一致arm64-v8a 的 qmake 不要去编 armeabi-v7a 的库链接器会直接报架构不匹配。QMAKE_LIBDIR_MYSQL 指定的目录里必须有 libmysqlclient.soqmake 在做链接检查时会去找这个文件。如果 make 过程中报找不到 mysql.h优先确认 QMAKE_INCDIR_MYSQL 路径是否正确不要急着改 .pri 文件里的硬编码路径。注意要支持多个 ABI就得把 MySQL 库和 Qt 驱动插件在每个 ABI 下分别编译一遍产物分别存放APK 打包时才能按 ABI 目录各就各位。3.3 把两个 .so 放进 APK 的正确目录Qt 5.12 的 Android 打包机制会生成一个 android-build 目录原生库要放在 android-build/libs/ / 下而不是 assets 或其他目录。驱动插件其实不用手动 copy在 .pro 里声明 QTPLUGINQt 的 androiddeployqt 工具会自动收集插件。但 libmysqlclient.so 属于外部依赖工具不知道它存在必须手动处理。QT sql CONFIG mobility android { QTPLUGIN qsqlmysql ANDROID_ABIS arm64-v8a armeabi-v7a }QTPLUGIN 的作用是把指定的 Qt 插件随 APK 一起发布这里填 qsqlmysql工具会自动在已编译的 Qt 插件目录中找 libqsqlmysql.so 并打包。ANDROID_ABIS 声明目标架构如果只打 arm64-v8a老的 32 位设备装不上如果只打 armeabi-v7a64 位设备也能跑但性能吃亏。常见做法是两个都打上代价是 APK 体积变大对数据库应用来说这点体积可以接受。打完包之后用 unzip 命令确认 APK 里的库文件不要只看 android-build 目录就觉得完事了。unzip -l 列出的 lib/ 目录下应该同时出现 libqsqlmysql.so 和 libmysqlclient.so位置在 lib/arm64-v8a/ 或 lib/armeabi-v7a/ 下。如果只有前者运行时就会报缺依赖。unzip -l android-build/bin/*.apk | grep -E lib/.\.so4. 在 .pro 与 QSqlDatabase 里把连接跑通库和插件都进 APK 之后还差最后一公里让应用代码真正走到 QMYSQL 驱动上。这一章不涉及更多编译所有工作围绕 .pro 配置、数据库连接参数、Android 系统安全策略展开。很多人在这一步会卡在连接超时或驱动加载失败上其实不是代码逻辑问题而是 Android 9 之后的明文流量限制和 MySQL 服务端绑定地址没放开。先把 .pro 写对再确认连接参数最后排查系统级拦截顺序别乱。4.1 .pro 文件的最终形态.pro 文件里除了 QTPLUGIN还要显式加上 QT sql否则 QSqlDatabase 相关符号在链接阶段找不到。ANDROID_PACKAGE_SOURCE_DIR 用来指向自定义的 Android 资源目录如果你需要改 AndroidManifest.xml 里的属性这个变量会让你的 manifest 覆盖 Qt 默认生成的版本。这样一个最小可编译的 .pro 文件大概长这样QT sql network TARGET demoApp TEMPLATE app SOURCES main.cpp ANDROID_PACKAGE_SOURCE_DIR $$PWD/android-sources android { QTPLUGIN qsqlmysql ANDROID_ABIS arm64-v8a armeabi-v7a }TARGET 换成你自己的应用名ANDROID_PACKAGE_SOURCE_DIR 指向一个真实存在的目录目录里放 AndroidManifest.xml。如果没有这个目录去掉这一行也不影响编译但后面 4.3 节改明文流量配置时就得用 Qt 生成的 manifest路径比较绕。QTPLUGIN 与 ANDROID_ABIS 已经在上一章解释过这里不需要重复。4.2 QSqlDatabase 连接参数与日志验证连接 MySQL 的代码在桌面上写和在 Android 上写没有本质区别都是 QSqlDatabase 的套路。需要注意的只有一点驱动名必须是 QMYSQL且跟 APK 里打进去的插件名对齐。addDatabase 的第一个参数写错哪怕插件就在 APK 里也照样报 driver not loaded。下面这段代码建议直接放进一个公共数据库工具类里避免每个界面都来一套重复连接逻辑。#include QSqlDatabase #include QSqlQuery #include QDebug // 按连接名获取数据库句柄避免重复连接 bool openDatabase(const QString connName) { QSqlDatabase db QSqlDatabase::contains(connName) ? QSqlDatabase::database(connName, false) : QSqlDatabase::addDatabase(QMYSQL, connName); db.setHostName(192.168.1.20); // MySQL 服务器局域网地址 db.setPort(3306); // 默认端口 db.setDatabaseName(demo_db); // 数据库名 db.setUserName(app_user); // 应用专用账号 db.setPassword(change_me); // 密码只放测试环境生产走配置 // 连接超时设为 5 秒避免服务端不可达时 UI 卡死 db.setConnectOptions(MYSQL_OPT_CONNECT_TIMEOUT5); if (!db.open()) { qWarning() open db failed: db.lastError().text(); return false; } return db.isOpen(); }contains 检查避免同一个连接名重复注册database 第二个参数 false 表示不自动打开。QSqlDatabase 的连接对象不能随意跨线程用后面第 6 章会说连接名机制的细节。MYSQL_OPT_CONNECT_TIMEOUT 是 Qt MySQL 驱动支持的选项设 5 秒是移动网络下的合理值太长用户感知就是卡死太短弱网环境必挂。4.3 Android 9 明文流量限制真机连不上的第一个雷Android 9API 28开始系统默认禁止应用发起任何明文 HTTP 流量MySQL 的 3306 端口也在此列。表现是驱动加载正常、连接代码正确但 db.open() 一直失败日志里看不到具体异常原因。最容易判断的症状同一套 APK 装到 Android 8 上能连装到 Android 10 上连不上。开发阶段直接把 usesCleartextTraffic 打开是最快的验证方法。在 android-sources/AndroidManifest.xml 的 application 标签上加上这个属性重新打包即可。application android:labeldemoApp android:usesCleartextTraffictrue android:themestyle/AppTheme这个属性等于告诉系统允许所有明文流量仅限开发调试。生产环境不应该这么开正确做法是用 networkSecurityConfig 只放行你实际连接的服务器域名。注意一个问题MySQL 通常走 IP 地址而不是域名network_security_config 的 domain 标签不支持 IP 段所以内网 IP 直连的场景下要么用域名访问 MySQL要么只能开全局明文。很多团队在这个问题上纠结很久最后选择了给 MySQL 配域名再走网络安全配置。5. 避坑记录驱动加载失败的四个典型场景下面四条按出现频率排序前两个是打包结构问题后两个是服务端与网络配置问题。每条都是实际调试中见过的现象把现象、原因、解决串起来写你照着排查能省不少时间。5.1 驱动加载失败QMYSQL driver not loaded现象应用启动后调用 db.open()日志输出 QSqlDatabase: QMYSQL driver not loaded。代码逻辑没毛病.pro 里也加了 QT sql桌面版跑得好好的到了 Android 真机就报这个。原因QMYSQL 插件没进 APK或者进了 APK 但目录不对。Qt 在 Android 上运行时按 lib/ / 目录找插件如果 QTPLUGIN 里写的插件名与编译时不一致androiddeployqt 收集不到对应 .so。还有一种情况Android 真机是 32 位 ARMAPK 里只打了 arm64-v8a 的插件。解决先 unzip -l 检查 APK 里有没有 lib/armeabi-v7a/libqsqlmysql.so再看 QTPLUGIN 写的是不是 qsqlmysql。如果 QTPLUGIN 没问题检查 ANDROID_ABIS 是否覆盖了目标设备架构。把两个 ABI 都打上是避免这类问题最简单的方式。5.2 动态库依赖缺失dlopen failed 找不到 libmysqlclient.so现象libqsqlmysql.so 已经确认在 APK 里但启动时崩溃日志里有 dlopen failed: library libmysqlclient.so.18 not found 之类的报错。原因libqsqlmysql.so 编译时链接的 SONAME 是 libmysqlclient.so.18APK 里拷进去的却是 libmysqlclient.soAndroid 的 linker 按 SONAME 找文件文件名对不上就报缺失。这算是最隐蔽的一个坑因为你在 android-build 目录里能看到 .so 文件以为万事大吉。解决上一章 readelf -d 查出来的 SONAME 就是关键。把 /opt/mysql-android-arm64/lib/ 下对应的完整版本号 .so比如 libmysqlclient.so.18一并放进 APK 的 lib/ / 目录linker 就能找到了。如果想省掉带版本号的库可以在编插件时给链接器传 -Wl,-soname,libmysqlclient.so把依赖名固定成不带版本号的但改动编译参数要重新走一遍不如拷文件省事。5.3 真机连不上 MySQLConnection refused 还是 timeout现象模拟器上连接 MySQL 一切正常换到真机就连不上按钮点了没反应最后在应用日志里看到 QSqlError 报 Connection refused 或 timeout。WiFi 是同一个路由器PC 上命令行连 MySQL 也正常。原因三个层面都要查。第一MySQL 的 bind-address 默认监听 127.0.0.1只允许本机连接Android 设备通过网络访问自然被拒。第二服务器防火墙或云安全组没有放行 3306 端口。第三MySQL 用户表的 host 字段只允许 localhostapp_userlocalhost 挡掉了来自局域网的连接。解决MySQL 配置文件 my.cnf 里把 bind-address 改成 0.0.0.0重启 MySQL 服务防火墙放行 3306然后在 MySQL 里创建允许任意来源的应用账号。注意这种账号权限要收敛只给业务库的增删改查不要给 DROP、TRUNCATE 这类高危权限。下面这条 SQL 专门给应用建账号CREATE USER app_user% IDENTIFIED WITH mysql_native_password BY Pssw0rd; GRANT SELECT, INSERT, UPDATE, DELETE ON demo_db.* TO app_user%; FLUSH PRIVILEGES;host 用 % 表示任意 IP适合应用服务器和移动端直连场景。如果公司安全规范不允许 % 账号也可以限定网段比如 192.168.1.%Android 设备连的是局域网时够用。5.4 MySQL 8 认证插件不兼容caching_sha2_password cannot be loaded现象数据库连接在握手阶段失败错误信息类似 Authentication plugin caching_sha2_password cannot be loaded。应用代码没变服务端从 MySQL 5.7 升到 MySQL 8 后就这样了。原因MySQL 8 默认认证插件是 caching_sha2_password交叉编译的旧版 libmysqlclient 不认识这个插件握手协议走不到密码验证那一步。解决有两种思路。要么在服务端把应用账号改成 mysql_native_password命令就是我上一小节写的那条 CREATE USER加一句 IDENTIFIED WITH mysql_native_password 指定老认证。要么换一个支持 caching_sha2_password 的客户端库mariadb-connector-c 较新版本对 MySQL 8 的支持比官方 mysql-connector-c 6.1 更友好。如果服务端是公司统一管理的生产库改账号认证方式可能触发合规审批那我就建议从客户端库版本入手重新编译一个版本对应的 libmysqlclient。6. 进阶做法把直连改成可靠的连接封装数据库连接写到能连上只是第一步移动端网络不稳定WiFi 切换、进电梯、隧道里断网都是常态。直接把 QSqlDatabase 对象存在全局变量里用一旦断网重连就是灾难现场因为 Qt 不会帮你自动重连。我的习惯是封装一个最小连接管理器把连接名、超时重试、断线检测放在一起。核心思路是连接名机制Qt 允许用同一个名字多次获取数据库句柄第一次创建之后获取的都是同一个连接这样重连时不会堆积大量无用连接。class DbKit { public: // 保证连接存在并打开失败时返回错误文本 static QString ensureOpen(const QString connName) { QSqlDatabase db QSqlDatabase::contains(connName) ? QSqlDatabase::database(connName, false) : QSqlDatabase::addDatabase(QMYSQL, connName); db.setConnectOptions(MYSQL_OPT_CONNECT_TIMEOUT5); if (!db.isOpen() !db.open()) return db.lastError().text(); return QString(); // 空串表示成功 } };调用方每次执行 SQL 前先跑一次 ensureOpen连接断了就重新 open。配合 QTimer::singleShot 做退避重试第一轮等 1 秒、第二轮等 3 秒、再往后等 10 秒避免弱网环境下疯狂重连把服务器打爆。这里有个讲究不要在创建连接的线程之外直接使用同一个 QSqlDatabase 对象Qt 文档说得很清楚一个连接被多个线程同时访问属于未定义行为。正确做法是在工作现场里用连接名拿到句柄再操作。多线程执行查询时每条线程用 QSqlDatabase::database() 拿到自己的句柄副本但底层连接是同一个。Qt 内部会用互斥锁保护连接操作只是高并发下锁竞争严重大量数据写入场景可以用多连接分摊。连线串、锁、超时都摆平之后再考虑传输加密。MySQL 8 服务端配置 TLS 证书后Qt 的 QMYSQL 驱动在 connectOptions 里可以指定 SSL 相关参数但不同版本 Qt 支持的选项名有差异最直接的办法是先用抓包工具确认握手是否为加密流量别想当然认为连上了就是安全的。这套流程我第一次做的时候卡在 SONAME 那条上整整半天后来养成的习惯是每次换 NDK 或换驱动版本先 readelf、再打包、再 unzip 验收三步走完才放心往机器上装。从那以后我交付的每个项目都强制走一遍这个流程。希望帮到你。本文还有配套的精品资源点击获取
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/22 21:16:40
Tushare免费A股数据接口实测:从注册到策略回测全流程
2026/10/22 21:16:40
预训练模型微调实战:从原理到踩坑经验总结
2026/10/22 21:16:40
预训练模型学习全路径:从原理到微调实战与踩坑指南
2026/10/22 22:11:55
GDB调试实战:从崩溃现场到core文件的高效定位指南
2026/10/22 22:11:55
CityEngine规则库:可编程城市建模的CGA核心实践
2026/10/22 22:11:55
分治算法核心解析:从递归思想到排序实战与调试技巧
2026/10/22 22:11:55
Windows下用MinGW-w64编译OpenCV 4.10实战指南
2026/10/22 22:11:55
抓包篡改金额原理与防御:Web接口参数校验实战指南
2026/10/22 22:06:55
LVGL多国语言字库制作全流程:从字符提取到避坑实践
2026/10/22 0:01:24
电商智能运营体系从0到1:架构设计、落地步骤与避坑指南
2026/10/22 0:01:24
压缩空气储能与热网热惯性联合优化调度:热电联产系统建模与求解
2026/10/22 0:01:24
若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定
2026/10/21 19:05:16
OpenResearch实战:Claude Code、Codex、OpenCode、Cursor 工作流搭建与故障排查
2026/10/22 17:15:21
知识管理核弹:用Obsidian构建第二大脑的工程化方案(TaoToken 统一 Key 接入版)
2026/10/21 19:05:04
金融AI落地指南:从调研报告到技术选型与工程实践
2026/10/22 5:06:15
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/22 2:07:18
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/21 19:01:08
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)