Qt Android 连接 MySQL:用 HTTP API 替代直连的完整实践

发布时间:2026/10/22 3:32:32

Qt Android 连接 MySQL:用 HTTP API 替代直连的完整实践 简介面向需要在 Qt 5.12.1 Android 应用中接入 MySQL 数据库的开发者这份资源围绕 MySQL 驱动的交叉编译、插件封装与 APK 打包提供完整参考方案。内容基于 Ubuntu/Windows 双平台环境覆盖 NDK 工具链配置、qsql_mysql 插件修改、QSqlDatabase 连接参数设置等关键环节并附有安全传输与性能优化建议适合具备 Qt 基础、希望扩展 Android 本地数据存储能力的中级开发者。压缩包共 934 个文件大小 16.51MB包含 302 个 h 头文件、54 个 c 源文件、131 个 txt 说明文档以及 Makefile、configure 脚本等编译辅助文件便于对照源码理解驱动构建流程。当前已有 777 人学习此类配置过程常因 NDK 版本与 MySQL 库不匹配而失败资料中列出的路径和修改项能显著降低踩坑概率。通过研读包内文件可掌握将 QMYSQL 驱动加载到 Android 的完整链路实现远程 MySQL 访问为应用提供高效持久化能力。1. qt android mysql 不是「三连」而是「三层」qt android mysql 这三个词摆在一起大多数人第一反应是在安卓手机上用 Qt 写个 App然后像桌面程序一样直接连 MySQL。这个方向不是不行而是绝大多数人会在第一步就卡在驱动上。我接过一个模拟项目X一开始也按桌面习惯配 QMYSQL结果 Android 端一直报 driver not loaded折腾两天后发现不是代码问题是这一条路本身就不适合移动端。这篇文章不打算教你把 MySQL 塞进手机而是给你一条最少踩坑的路线MySQL 在服务端一个轻量接口服务在中间Qt Android 客户端只发 HTTP 请求。这种架构不挑 Qt 5 还是 Qt 6也不挑 MySQL 8 还是兼容 MySQL 协议的社区版数据库三五天就能跑通一个完整 Demo。如果你要做的方向是工控数据展示、内部信息系统的移动查询端、课设里的设备管理类 App这套方案能直接用。如果你幻想着让千万台手机直连数据库 3306 端口我建议你先把这篇文章看完再决定要不要继续。下面进入正题。2. 先立架构Android 直连 MySQL 为什么容易翻车2.1 Android 上跑 QMYSQL 的三座山Qt 的 SQL 模块本身是好用的。在 Windows 或 Linux 桌面上QSqlDatabase::addDatabase(QMYSQL)一行就能拿到数据库句柄前提是 Qt 安装包里带了qsqlmysql插件。传统桌面开发者的思维定势就是我本地能连那手机上也一定能连。Android 上不是这个逻辑。第一座山是客户端库。Qt 的 MySQL 插件底层需要链接 MySQL 官方客户端库或者 MariaDB Connector/C而 Android NDK 的 sysroot 里默认没有这个库。第二座山是驱动插件本身。Qt For Android 的发布包里SQL 驱动通常只保证 SQLiteQMYSQL 插件经常不在里面drivers()列表里只有 QSQLITE。第三座山是 ABI 匹配。就算你把桌面端编译出来的libqsqlmysql.so拿到 Android 上armeabi-v7a、arm64-v8a、x86_64这些 ABI 之间的二进制不能互通再加上 MySQL 8 默认的caching_sha2_password认证插件老版本客户端库直接握手失败。先看一段在桌面上能跑通、到了 Android 大概率翻车的代码#include QSqlDatabase #include QSqlQuery #include QSqlError #include QDebug bool openDatabase() { QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL); db.setHostName(192.168.1.20); db.setPort(3306); db.setDatabaseName(qt_demo); db.setUserName(qt_app); db.setPassword(change-me); if (!db.open()) { qDebug() db.lastError().text(); return false; } QSqlQuery query(db); query.exec(SELECT id, name FROM device); while (query.next()) { qDebug() query.value(0).toInt() query.value(1).toString(); } return true; }这段代码在桌面平台没有任何问题但放到 Android 上最常见的结果是QSqlDatabase: QMYSQL driver not loaded。这不是你setHostName写错了也不是密码错而是 Qt 在运行时找不到可加载的驱动插件。就算你从某个渠道拿到一个带qsqlmysql.so的构建它打包时也必须带上libmysqlclient.so或libmariadb.so少一个依赖就启动失败。提示在 Android 上调试驱动问题时先用qDebug() QSqlDatabase::drivers()打印可用驱动列表。如果列表里只有 QSQLITE说明 QMYSQL 插件根本没进包后续排查方向就不应该再对着连接参数。桌面上open()是建立 TCP 连接Android 上open()之前先要过驱动加载这一关。这个差异让我第一次做移动端时吃了大亏所以现在遇到类似需求我第一句话都是劝对方别在客户端碰 QMYSQL。2.2 三种连接方案的取舍直连、长连接、HTTP API把手机连接到 MySQL绕来绕去无非三条路一是客户端直连数据库端口二是客户端连一个自研 TCP 长连接服务让服务端去连 MySQL三是客户端走 HTTP/HTTPS 接口由接口服务读写数据库。我一般会直接推荐第三条下面这个表能看懂为什么方案App 到数据端的链路数据库凭据存在哪风险点适用场景客户端直连 QMYSQLApp - MySQL 3306App 安装包里驱动难配、账号泄露、连接易断局域网桌面工具、内网调试自研 TCP 长连接服务App - Server - MySQL服务端协议要自己设计粘包断包要处理实时指令下发业务固定HTTP/HTTPS APIApp - API - MySQL服务端请求延迟比直连略高绝大多数业务和展示场景有人可能觉得 HTTP API 不够“底层”多了一层性能损耗。但移动端不是高频数据库写入场景Qt App 的常态是拉列表、填表单、看状态一个只读查询接口的响应时间在局域网内完全可以做到 100 毫秒以下。真正要命的不是这一层损耗而是直连方式在弱网环境下的表现。Android 系统会随时回收后台进程App 一旦退到后台再切回来之前建立的长连接早就被系统或者 MySQL 服务端断掉了。你重新open()一次TCP 握手加上认证在网络抖动时可能卡两三秒。HTTP 接口则天然是短连接请求一次、拿到结果、连接结束即使断网客户端也只需要提示“网络不可用”不会残留一堆半死不活的状态。我后来做移动端项目时的习惯是无论数据库是 MySQL、PostgreSQL 还是 SQLite只要是公网或者跨设备的场景一律在中间加接口层。Qt 客户端永远只认识 JSON不认识 SQL。这种情况下即便今天把 MySQL 换成别的数据库客户端几乎不用动这个收益在维护期会放大很多倍。3. 本地复现先把 MySQL 和接口服务跑起来3.1 用 MySQL 8 建库建表最小可查询的数据这一章我们把链路从零跑通。第一步先准备好 MySQL没有本地数据库环境的直接起一个mysql:8容器最省事。命令如下docker run -d --name qt-mysql \ -e MYSQL_ROOT_PASSWORDroot \ -e MYSQL_DATABASEqt_demo \ -e MYSQL_USERqt_app \ -e MYSQL_PASSWORDqt_app_pass \ -p 3306:3306 \ mysql:8这条命令里最关键的不是-p 3306:3306而是MYSQL_USER和MYSQL_PASSWORD这对环境变量。容器初始化时会自动创建一个qt_demo库并把qt_app这个账号授权到这个库上。注意这个账号不是全局管理员只是业务账号后面接口服务就用它连数据库。表结构按“设备列表”这种最常见的业务来设计存设备编号、名称、状态和最后在线时间。SQL 如下CREATE TABLE device ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, code VARCHAR(32) NOT NULL UNIQUE COMMENT 设备编号, name VARCHAR(64) NOT NULL COMMENT 设备名称, status TINYINT NOT NULL DEFAULT 0 COMMENT 0离线 1在线, last_seen DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 最后上线时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO device (code, name, status) VALUES (D001, 温度传感器, 1), (D002, 门禁控制器, 0), (D003, 电表, 1);status用TINYINT而不是ENUM是因为移动端展示时经常需要把这几种状态映射成文字如果数据库端用枚举以后加一个状态就要改表结构接口层和客户端都要跟着动不划算。charsetutf8mb4是必须的否则中文字段名、中文备注都能存但客户端拿到中文数据之后很可能会在 JSON 解析那一环出问题。注意容器初始化创建的qt_app账号只能保证 Demo 能跑。真实环境里应该把权限收敛到只读等接口层写完再执行GRANT SELECT ON qt_demo.device TO qt_app…不需要给业务账号DROP和ALTER权限。执行建表语句时如果 SQL 文件在宿主机上可以用下面这条命令把文件灌进容器docker exec -i qt-mysql mysql -uroot -proot qt_demo schema.sql执行完以后用SHOW TABLES;确认一下device表在再SELECT * FROM device;看三条数据是否都在。尽量在数据库这一层就把问题看清楚不要带着疑问直接跳到接口开发。3.2 写一个只读数据接口PHP PDO 单文件实现接口层我一般先用 PHP PDO 写一个单文件版本原因是部署太方便不需要编译有一个 PHP 环境就能跑。你用 Node、Python、Go 也完全可以核心思路只有一个服务端持有 MySQL 账号客户端拿到的只是一个普通 HTTP 地址。新建api/device.php内容如下?php header(Content-Type: application/json; charsetutf-8); $pdo new PDO( mysql:host127.0.0.1;dbnameqt_demo;charsetutf8mb4, qt_app, qt_app_pass, [ PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE PDO::FETCH_ASSOC, PDO::ATTR_TIMEOUT 5, ] ); $stmt $pdo-query(SELECT id, code, name, status, last_seen FROM device ORDER BY id); $rows $stmt-fetchAll(); echo json_encode([code 0, data $rows], JSON_UNESCAPED_UNICODE);这个文件做的事情很简单连接 MySQL查询device表把结果包成统一 JSON 结构返回。code字段表示业务成功或失败data才是真正的数据客户端只看这两个字段就够了。PDO::ATTR_TIMEOUT是连接超时时间设成 5 秒是为了防止 MySQL 挂掉的时候接口一直卡着。真正的查询超时要在 MySQL 端或 PHP 配置里控制这里先不做过多展开。charsetutf8mb4必须写如果不写即使数据库表是utf8mb4PDO 也可能用utf8连接遇到生僻字或表情符号就会丢数据。接口写好后直接启动 PHP 内置服务器来调试php -S 0.0.0.0:8080 -t api0.0.0.0表示监听所有网卡这样才能让局域网里的真机访问到。如果只在电脑本机调试127.0.0.1就行。启动后先用curl验证接口不要急着打开 Qtcurl -i http://127.0.0.1:8080/device.php正常返回的 JSON 大概长这样{code:0,data:[{id:1,code:D001,name:温度传感器,status:1,last_seen:2025-04-01 10:00:00}]}能看到这一行说明 MySQL 到接口这一段已经通了。如果返回的是 500去 PHP 的错误日志里看 PDO 报的什么错最常见的就是账号密码不对、host 写错、认证插件不兼容。这一层是整条链路的地基地基没打好锅会一路甩到 Qt。为什么要单文件演示因为新手最容易被框架绕晕。接口层后面可以换成任何成熟框架但最小可运行模型越简单越好。单文件接口的问题也很明显没有参数校验、没有权限控制、没有日志。这些问题放到最后一章再说这里先把链路拉通。4. Qt Android 客户端从 HTTP 到 JSON 的最小可运行封装4.1 用 QNetworkAccessManager 封装 DeviceApi接口已经通了下面写 Qt 客户端。我建议把网络请求放在 C 类里不要直接在 QML 里写XMLHttpRequest因为 C 这边可以做超时、重试、打日志QML 里做这些事会别扭很多。新建一个DeviceApi类头文件如下#ifndef DEVICEAPI_H #define DEVICEAPI_H #include QObject #include QVariantList #include QNetworkAccessManager #include QNetworkReply class DeviceApi : public QObject { Q_OBJECT Q_PROPERTY(QVariantList devices READ devices NOTIFY devicesReady) public: explicit DeviceApi(QObject *parent nullptr); QVariantList devices() const; Q_INVOKABLE void fetchDevices(const QString baseUrl); signals: void devicesReady(const QVariantList devices); void requestFailed(const QString message); private slots: void handleReplyFinished(QNetworkReply *reply); private: QNetworkAccessManager m_manager; QVariantList m_devices; }; #endif实现文件#include DeviceApi.h #include QJsonDocument #include QJsonObject #include QJsonArray #include QNetworkRequest #include QUrl DeviceApi::DeviceApi(QObject *parent) : QObject(parent) { } QVariantList DeviceApi::devices() const { return m_devices; } void DeviceApi::fetchDevices(const QString baseUrl) { QUrl url(baseUrl QStringLiteral(/device.php)); QNetworkRequest request(url); request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral(application/json)); request.setRawHeader(Accept, application/json); QNetworkReply *reply m_manager.get(request); connect(reply, QNetworkReply::finished, this, DeviceApi::handleReplyFinished); } void DeviceApi::handleReplyFinished(QNetworkReply *reply) { reply-deleteLater(); if (reply-error() ! QNetworkReply::NoError) { emit requestFailed(reply-errorString()); return; } const QByteArray body reply-readAll(); const QJsonDocument doc QJsonDocument::fromJson(body); if (!doc.isObject()) { emit requestFailed(QStringLiteral(JSON 格式不是对象)); return; } const QJsonObject obj doc.object(); if (obj.value(QStringLiteral(code)).toInt() ! 0) { emit requestFailed(obj.value(QStringLiteral(message)) .toString(QStringLiteral(业务错误))); return; } m_devices.clear(); const QJsonArray arr obj.value(QStringLiteral(data)).toArray(); for (const QJsonValue value : arr) { m_devices.append(value.toVariant()); } emit devicesReady(m_devices); }这里要说明几个关键选择。QNetworkAccessManager是成员对象而不是局部变量局部变量会在请求没结束时被销毁导致回调莫名其妙成员对象生命周期跟DeviceApi一致适合挂在 QML 里长期使用。reply-deleteLater()是必须的否则每次请求都会泄漏一个QNetworkReplyApp 开着开着内存就往上涨。QJsonDocument::fromJson默认按 UTF-8 解析接口端返回的也是 UTF-8 JSON所以两边都不用额外转码。code不是 0 时表示业务失败这里把错误信息透传出去QML 端能直接显示。真正要展示的数据统一塞到QVariantList里方便 QML 的 ListView 直接绑定。提示如果以后接口要改成 POST只需要把m_manager.get(request)换成m_manager.post(request, body)并在QNetworkRequest上把Content-Type设成application/json。但写操作接口必须加权限校验不能像读接口这样裸奔。4.2 QML 调用与 Android 权限清单C 类写好后在main.cpp里注册成 QML 类型#include QGuiApplication #include QQmlApplicationEngine #include DeviceApi.h int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); qmlRegisterTypeDeviceApi(DeviceApi, 1, 0, DeviceApi); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral(qrc:/Main.qml))); return app.exec(); }如果项目是用 qmake 管理的记得在.pro文件里加上QT quick network CONFIG c17network模块不加QNetworkAccessManager编译不过。到这一步QML 里就能直接拉数据了import QtQuick 2.15 import QtQuick.Controls 2.15 import DeviceApi 1.0 ApplicationWindow { width: 480 height: 720 visible: true DeviceApi { id: api onRequestFailed: (message) resultText.text message } Column { spacing: 12 anchors.fill: parent Button { text: 刷新设备 onClicked: api.fetchDevices(http://10.0.2.2:8080) } Text { id: resultText width: parent.width } ListView { width: parent.width height: 500 model: api.devices delegate: Text { text: modelData.name [ modelData.code ] 状态: modelData.status } } } }这里10.0.2.2是 Android 模拟器里宿主机地址的专用别名。模拟器里的 App 访问宿主机 HTTP 服务时不能用127.0.0.1因为127.0.0.1指向的是模拟器自己。真机调试时要把这个地址换成电脑在局域网里的 IP比如http://192.168.1.100:8080。Android 端还差一个关键文件AndroidManifest.xml里的网络权限。没有 INTERNET 权限所有网络请求都会失败。uses-permission android:nameandroid.permission.INTERNET/ application android:usesCleartextTraffictrue ...usesCleartextTraffictrue表示允许 HTTP 明文请求。Android 9 以后默认禁止明文流量不加这个属性接口虽然是 HTTP 也访问不了。这个开关只是开发阶段用来保命正式包一定要走 HTTPS并且把这个属性去掉用network_security_config.xml来做更细粒度的控制。4.3 地址别写死模拟器、真机和生产地址的三态配置很多新手把http://10.0.2.2:8080直接写在 QML 的按钮事件里Demo 能跑但换到真机上又改一遍代码上了生产再改一遍。这属于自己给自己挖坑。我的习惯是在 C 层做一个编译期配置#include QtGlobal namespace ApiConfig { #ifdef Q_OS_ANDROID const QString baseUrl QStringLiteral(http://10.0.2.2:8080); #else const QString baseUrl QStringLiteral(http://127.0.0.1:8080); #endif }QML 那边只传ApiConfig::baseUrl给fetchDevices具体地址是谁由当前平台决定。真机调试时临时加一个Q_OS_ANDROID分支里替换成192.168.x.x比在 QML 文件里翻来覆去改字符串稳得多。还有一种更规范的做法把接口地址放到QSettings里App 启动时从配置中心拉一次这样发布包和运行环境完全解耦。不过对中小型项目来说编译期宏已经够用了。重点是数据库密码永远不能出现在这个配置里因为 App 包一旦被人解开配置项全部暴露在阳光下。MySQL 的账号只在接口服务端保存这比任何代码混淆都安全。5. 避坑清单Qt Android MySQL 链路最容易翻车的五个地方5.1 现象Android 上报QMYSQL driver not loaded这个问题在第二章已经提到过。它的特征非常明显同一段代码桌面上跑得好好的一到 Android 模拟器或真机就提示驱动不可用。新手第一反应是去检查setDatabaseName和setHostName改来改去没有任何效果。原因有两个层面。第一Qt For Android 的发行包里可能根本没有编译qsqlmysql插件第二就算你手动补了插件它依赖的 MySQL 客户端库也要跟着进 APK还必须在目标 ABI 下匹配。这条路上的血泪经验是不要试图把桌面环境里的plugins/sqldrivers整个拷进 Android 工程ABI 不匹配会直接导致段错误。解决办法很简单放弃在 Android 端直连改走 HTTP API。你会在 Qt 客户端里删掉所有和QSqlDatabase相关的代码驱动问题自动消失。如果你就是要在局域网里直连那你要做好交叉编译 MariaDB Connector/C 和 Qt 驱动插件的准备这个工程量一个月打底而且每次升级 NDK 或 Qt 都要重新来一遍。5.2 现象接口层报Access denied或The server requested authentication method unknownPHP PDO 连接 MySQL 8 时偶尔会报认证方式未知。我在模拟项目X 里第一次遇到时也很懵数据库账号密码明明是对的桌面客户端能连PHP 就是连不上。原因是 MySQL 8 默认的caching_sha2_password认证插件旧版本 PHP 的mysqlnd驱动不认识。解决办法有两种一是升级 PHP 版本到支持这个插件的版本二是给业务账号单独指定兼容的认证插件CREATE USER qt_applocalhost IDENTIFIED WITH mysql_native_password BY qt_app_pass; GRANT SELECT ON qt_demo.device TO qt_applocalhost;注意这里只改qt_app这一个账号不要去全局改default_authentication_plugin否则会把整个 MySQL 实例的认证行为都带偏。如果接口跑在容器外面而 MySQL 跑在容器里面用户 host 可能还要写成qt_app%连接的时候再用127.0.0.1指定 TCP 端口。账号匹配顺序是 MySQL 的隐藏逻辑先localhost后%出错时用SELECT user, host FROM mysql.user;确认一下创建出来的账号到底对应哪个 host。5.3 现象模拟器里访问宿主机接口超时curl 在电脑上访问接口是通的Qt Android 模拟器里跑起来却一直超时。这个坑十个人有九个人踩。原因是 Android 模拟器内部的127.0.0.1指向模拟器自己而不是你的电脑。宿主机在模拟器网络里被映射成10.0.2.2。所以接口地址不是http://127.0.0.1:8080/device.php而是http://10.0.2.2:8080/device.php。如果是真机调试问题又不一样。真机和电脑必须在同一个局域网里接口地址要填电脑的实际 IP比如http://192.168.1.100:8080/device.php。同时电脑防火墙要放行8080端口否则手机能 ping 通电脑但 TCP 连接到 8080 照样超时。排查这类问题时不要先在 Qt 里找原因先用手机浏览器打开接口地址能看 JSON 就说明网络通再看 Qt 代码。5.4 现象Android 9 以后请求直接报明文流量被禁止接口地址正确、网络也通但 Qt 客户端收到Protocol failure或者系统日志里出现CLEARTEXT communication not permitted这就是 Android 的明文流量策略在拦路。Android 9API 28开始默认禁止 App 使用 HTTP 明文流量。解决办法有两种。开发阶段最省事的做法是在AndroidManifest.xml的application标签上加android:usesCleartextTraffictrue。这个方法只适合调试正式发布的 App 如果还在用 HTTP等于把数据光着身子扔进公共网络。正式环境应该用 HTTPS并在network_security_config.xml里只信任你自己的域名。如果对证书配置不熟可以先在 Qt 端用QSslConfiguration加载自签名证书但不要为了省事关闭证书校验。我见过有人直接ignoreSslErrors这个习惯比密码泄露还危险。5.5 现象接口返回中文乱码QJsonDocument 解析失败数据库里查出来的中文是好的curl 看到的 JSON 也是好的一到 Qt 客户端就乱码或者QJsonParseError。先检查接口返回头有没有charsetutf-8。PHP 代码里header(Content-Type: application/json; charsetutf-8)这一行不能省。再看 MySQL PDO 连接的 DSN 里有没有charsetutf8mb4。如果这两处都对最后检查 PHP 文件本身是不是带 BOM 的 UTF-8某些编辑器会在文件开头塞三个不可见字节导致接口返回的 JSON 最前面多一段乱码Qt 端fromJson自然解析失败。解决办法编辑器和数据库统一用 UTF-8 无 BOM。Qt 端收到字节数组后先qDebug() body看看原始内容再决定是解析问题还是编码问题。不要在客户端反复toUtf8、toLocal8Bit转来转去源头对了链路就干净了。6. 进阶三层验证法把 Qt、接口、MySQL 的责任边界划清楚6.1 三层验证法先 MySQL再 API最后 Qt出问题时最怕所有人都在猜。我养成的习惯是自底向上验证每一层留下明确证据。第一层验证 MySQL 本身。用命令行执行SELECT * FROM device;能返回数据就说明库表没问题。第二层验证接口。curl -i http://127.0.0.1:8080/device.php看 HTTP 状态码和 JSON 内容。第三层才轮到 Qt。如果 Qt 端报错先用adb logcat -s Qt过滤日志看QNetworkAccessManager有没有输出错误码。curl -s http://10.0.2.2:8080/device.php | head adb logcat -s Qt:D | tail -50第一行命令在电脑终端里执行能验证宿主机视角的接口通断。第二行命令在 Qt 客户端运行后执行能看到崩溃前 Qt 打的日志。两层日志一对比问题到底出在网络、接口还是 MySQL基本一目了然。这个方法比在代码里到处加qDebug()更快。6.2 上线前五个默认项如果这个方案要投入正式环境我默认会把下面五件事全部做掉项目默认配置原因MySQL 账号只授权SELECT/INSERT/UPDATE禁止DROP/ALTER防脱库接口层参数白名单校验拒绝拼接 SQL防注入传输层全链路 HTTPS关闭明文流量防抓包泄露接口限流每个 token 每秒最多 N 次请求防止恶意刷接口拖垮 MySQL日志只记设备号不记密码和完整 token防日志泄密这五条不需要第一天就全做但如果这个 Qt Android MySQL 方案要跑半年以上少一条都会在某个深夜变成事故。我现在的习惯是拿到需求先问一句你是想让手机直接连 MySQL 吗如果答案是肯定的我会把这个标题拆成 Qt Android HTTP API MySQL 四层。这个习惯帮我避开了不少无用功希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/22 3:32:32

基于YOLO的水下鱼类多目标检测系统实战:从数据到部署

我前段时间帮一个海洋牧场项目做了套鱼群监测系统,核心目标就一件事:对着水下视频画面,把游来游去的鱼实时框出来、数清楚、分好类。前后折腾了三个多月,踩了不少坑,也总结出一套相对完整的流程。今天这篇就把整个“基…

2026/10/22 3:32:32

Qt Android直连MySQL:驱动交叉编译与六大避坑实践

简介:面向基于 Qt 5.12.1 的 Android 与 MySQL 集成开发场景,这份资源包集中提供编译 QMYSQL 驱动所需的源码、配置与构建辅助文件,帮助开发者在 Windows/Ubuntu 环境下完成 NDK 交叉编译,解决 Android 端默认缺少 MySQL 驱动的常…

2026/10/22 3:32:32

LangChain Agent 不稳定?试试 Harness Engineering 这套外壳

很多人在 LangChain 里写 Agent,写完第一版感觉特别顺,模型一调、工具一接,好像什么都能干。但一旦丢到真实业务里,问题就全冒出来了:任务稍微绕一点就跑偏,同样的输入有时候对有时候错,Token 消…

2026/10/22 4:22:37

Redis作为AI短期记忆中枢的三大协同模式与数据建模实践

1. 为什么 Redis 突然成了 AI 系统的“记忆中枢”?最近在几个技术交流群里,频繁看到有人发截图:某大模型服务的响应延迟从 800ms 骤降到 120ms,监控图上 Redis 的 QPS 曲线同步冲高;另一份内部架构文档里,“…

2026/10/22 4:22:37

河北专升本汇编第四章:伪指令与程序设计核心考点突破

1. 为什么第四章是专升本汇编的分水岭1.1 从“能看懂”到“能写出来”的跨越河北专升本计算机专业的微机原理与接口技术这门课,很多人的复习轨迹是这样的:前三章学CPU结构、寄存器、存储器寻址,跟着视频课划划重点,做做选择题&…

2026/10/22 4:22:37

MediaPipe手部关键点实时跟踪:从零上手到手势识别实战

1. 从零上手MediaPipe手部关键点实时跟踪第一次接触MediaPipe是在一个体感交互的Demo项目里,当时需要在不依赖深度相机的前提下,仅用普通RGB摄像头完成手部21个关键点的实时定位。试过OpenPose的手部模型,帧率掉到个位数;也试过自…

2026/10/22 4:22:37

可预见误用的工程化:基于仿真的SOTIF误用场景构建与风险闭环

我在一次内部项目评审会上,身边一位产品负责人问了我一个问题:车辆已经发出接管的明确警告,前方也是施工路段的锥桶,驾驶员却低头刷了十分钟手机,最后车辆冲进锥桶堆,这个锅到底算谁的?当时会议…

2026/10/22 4:22:37

uniapp+Java选课系统实战:含MySQL事务、RBAC权限与真机调试

简介:本资源是一套面向高校计算机专业本科生的毕业设计/课程设计级移动端选课系统完整实现,聚焦教育信息化场景,解决传统选课流程中学生选退课不便、教师课程管理低效、教务数据分散等实际问题。资源包共978个文件,32.87MB&#x…

2026/10/22 4:17:36

从零手写AI艺术生成器:扩散模型DDPM原理与实现

朋友问我用什么AI工具画画,我一开始支支吾吾答不上来。不是不会用,而是我就会按几个按钮,底下的原理一问三不知。后来我较上劲了:直接用 Python 和深度学习,从零写一个 AI 艺术生成器——不调现成的图像生成接口&#…

2026/10/21 4:53:08

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑