最近由于要开发个需要用到低功耗蓝牙(BLE)的安卓app,因此需要搭建qt的安卓开发环境。
遇到的不少问题搜不到啥资料,只有自己去摸索尝试,但仍然有问题不知道怎么解决。被这环境搭建折腾得不轻、时间也折腾得不短,记录一下,给自己当个备忘,对有同样需求的人希望能有所帮助。
先说一番折腾后最终使用的版本:Qt6.5 + openjdk17 + cmdline-tools 16。
之前也没搭建过着环境,于是在网上开始搜教程,一开始选的是最后的可以本地安装的版本Qt 5.14.2,参照这教程 Qt | Qt For Android、Qt5.14.2安卓开发环境搭建详细步骤 配好的环境。蓝牙相关的程序写好后,开始是在win10电脑上调试,发现搜不到低功耗蓝牙设备,开始以为是USB蓝牙设备的问题,于是又在网上买了个,发现照样搜不到,网上一番查找才知道,mingw编译器搜不到ble设备、要用msvc编译器,而安装qt的时候只选了mingw,网上一番搜索,折腾了一阵没搞明白怎么添加组件的,于是想着干脆卸载掉装个Qt6了。
想着装个Qt6.5吧,由于之前没在线安装过,所以才觉得本地安装更方便、才抗拒在线安装,尝试了在线安装后才发现挺方便的,尤其是添加、卸载组件、换Qt版本,MaintenanceTool.exe中通通搞定了(虽然Qt5.14也有这维护工具,但是没弄明白怎么添加msvc编译器)。在线安装时,若直接双击qt-online-installer-windows-x64-online.exe安装比较慢的话,就可以使用镜像,cmd中cd到其所在的目录:
.\qt-online-installer-windows-x64-online.exe --mirror https://mirrors.ustc.edu.cn/qtproject
这是中科大的镜像速度不错,由于后来安装卸载过好些次,开始也用过阿里云(https://mirrors.aliyun.com/qt/)的镜像,觉得还是中科大的快一些。同样,维护的时候也可以使用镜像,换成MaintenanceTool.exe就行。关于镜像源可参考Qt 镜像源安装,文章中还有Qt官方推荐的镜像源的网址。
第一次安装Qt6.5的时候选了这些组件,后来觉得Qt Design Studio用不上,后面再装时就把其去掉了。
配置安卓开发环境比Qt5.14方便不少,只要安装下JDK,其他的SDK、NDK点几下鼠标就行。工具 - 外部 - 配置 - SDKs - Android,先点击JDK位置最右边的圆形打开Open JDK的下载网页,下载JDK11或17都行,然后点击浏览选择JDK的安装位置就可以了。Android SDK的路径,选择一个空的文件夹就行,然后点击设置SDK,正常情况下是会自动下载SDK、NDK这些的。下图是后面偶然发现的方法配置好安卓开发环境后的截图。
但是第一次安装好Qt6.5配置安卓开发环境时,意外发生了。正常情况是先1后2再3,但是出现1和2之后就没有然后了,试过好几次,就是不出现3,当时也不知道怎么解决,于是就只好试试安装Qt 6.8。
通过MaintenanceTool.exe很方便的就卸载了Qt 6.5、安装了Qt6.8,组件上和6.5时还是有不少区别的。
安装好后,配置安卓环境挺顺利,点击设置SDK后,一次出现了上面的1、2和3,没多久就安装好了。
于是就开始在Qt 6.8下开始写程序了,很快就发现个问题,之前Qt 5.14编译的程序在安卓上运行时,扫描蓝牙时是会自动弹出获取定位权限的对话框的,但是6.8编译的程序不弹框,导致根本没法扫描蓝牙,打印是没有定位权限导致的。试了下在手机的系统设置里手动授权app的定位权限,就可以正常扫描蓝牙了,但是不能自动获取肯定是个问题。
然后又是一番搜索,发现可以在C++中通过QLocationPermission手动添加权限(参考Application Permissions),添加好后,在安卓9上跑可以自动获取定位权限了,但是在安卓13上还是不能自动获取定位权限,所以不出意外也是不能扫描蓝牙,在Qt论坛上提问(lack of permission)也没找到办法。问题的回答中有提到:
Android 12 (API 31) and Later
Required permissions:
BLUETOOTH (normal permission - granted at install time)
BLUETOOTH_CONNECT (runtime permission - must be requested)
BLUETOOTH_SCAN (runtime permission - if scanning for devices)Android 6.0 (API 23) to Android 11 (API 30)
Required permissions:
BLUETOOTH (normal permission)
BLUETOOTH_ADMIN (normal permission - if you need to scan/pair)
ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION (runtime permission - required for scanning)
按这说法,安卓13(对应的是API 33 Android 系统与SDK和JDK版本对照表)是可以不需要定位权限了的,但是不知道为啥就是不能扫描蓝牙。
然后又是一番搜索,发现可以在左侧的项目中选中安卓的构建套件,点开“构建安卓APK”,点击“创建模板”,在AndroidManifest.xml中添加权限,可是由于创建工程时选的是Qt Quick Application,只能使用cmake,在创建模板时又出意外了:
在Qt论坛上也提问了(Unable to create the template),还是没有找到解决办法。
后来才发现创建工程时选Qt Quick Application(compat)可以创建qmake的工程,此时虽然能正常创建模板,添加了ACCESS_FINE_LOCATION、ACCESS_COARSE_LOCATION、BLUETOOTH、BLUETOOTH_ADMIN权限,但是还是不能自动获取定位权限。各权限的含义见Android Manifest.xml配置访问权限设置。
没办法,只能又是换版本了。于是又装上了Qt 5.14.2,但是总觉得心有不甘,想用新点的版本。
之前的搜索过程中就有看到过配置安卓环境时出问题,有的是cmdline-tools版本过新导致的,替换旧版本后就解决了。偶然在这篇文章 开发经验C++、QML、安卓常见问题合集 中看到了关于cmdline-tools的相关内容:
感觉Qt 6.5配置安卓开发环境又看到希望了。于是又重新安装了6.5,一开始装的OpenJDK11,然后用Everything搜sdk_definitions.json,把所有搜出来的文件里面的latest都改成了9.0,cmdline-tools 版本 和JDK版本的对于关系参考cmdline-tools 版本与其最小JDK关系。这次点击设置SDK后,总算是顺利安装好了,长舒了一口气,原来之前6.5配置不成功是cmdline-tools 版本太新的问题。
然后赶紧编译安卓试试看能不能自动弹出获取定位的对话框,Qt6.5 + openjdk11 + cmdline-tools 9,新建qmake项目(最低版本选5.15或6.2)或cmake项目在安卓9和13的手机上都能自动弹框获取定位、能正常扫描蓝牙。然后又试了Qt6.5 + openjdk17 + cmdline-tools 16也可以,只是明明cmdline-tools指定的版本明明是16,但是安卓配置环境页面显示的SDK版本却是12,在SDK管理器里显示的版本是16的。还试过指定cmdline-tools 17,SDK管理中已安装的如下所示,对比了16和17,除了cmdline-tools的版本不一样外,其它的都是一样的。
若同时安装了OpenJDK11和17,切换版本也很简单。先后安装OpenJDK11和17,安装时会自动设置环境变量,后安装的会覆盖前面安装的。要切换时,此电脑 - 右键属性 - 高级系统设置 - 环境变量 - 系统变量 - Path,将要使用的版本的安装路径放第一行,如当前是C:\Program Files\Eclipse Adoptium\jdk-17.0.14.7-hotspot\bin,要换成11时,将这一行内容换成11的安装路径即可,切换后应该是要注销一下系统才会生效,cmd中java -version查看当前生效的版本。
Qt6.8 + openjdk17 + cmdline-tools 16,可以看到NDK、Build-Tools和SDK平台的版本都比6.5的高一些。
想着试试看6.8下添加一下6.5的那些版本,是不是也能编译出的apk能自动弹框获取定位。
添加好后,构建安卓APK那能看到有两个版本的可选了,只有35.0.0 + android-35编译出来的apk是正常运行的,33.0.0 + android-35能成功编译,但是运行时闪退,其它组合编译直接报错了,也不知道是啥原因。所以这尝试是以失败告终了。
于是最终决定使用Qt6.5 + openjdk17 + cmdline-tools 16的组合。
由于电脑上装的是VS2022,而Qt6.5用的是MSVC 2019,因此需要在Visual Studio Installer中添加一下2019的相关组件,可参考 QT中MSVC2017无法使用的解决方法、qt5.14.2跟vs2022配置。
对比了6.5和6.8编译后生成的AndroidManifest.xml中关于权限的部分,差别还是挺大的,6.8的加上了版本的限制,感觉是6.8这样子的更合理,只是无奈实际上用不了啊。试过6.8创建qmake项目,然后创建模板添加相应的权限,手动添加的那些权限在AndroidManifest.xml中显示的和6.5的是一样的了,只是照样是不能自动弹框获取定位权限。
Qt6.5 + openjdk17 + cmdline-tools 16时,首次选安卓构建套件编译时,编译输出如下乱码信息,但是又能正常生成apk、安装后能正常运行;再次编译时又没有这些乱码了,搞不清这是为啥。Qt6.8时试过使用openjdk21,发现首次编译时也有乱码,只是乱码和这不同,换成openjdk17时又没有这乱码了。
需要更换运行的手机时:
低功耗蓝牙(ble)编程可参考:QT编程: 编写低功耗BLE蓝牙调试助手(Android系统APP)。
连接同一个目标蓝牙设备,安卓上通过蓝牙收发数据是正常的,但是win10上就是有问题,Qt5.14、Qt6.5、Qt6.8都试了,都不行,现象是:writeCharacteristic()写数据后,characteristicWritten信号没有被触发,但是对方又能收到数据,对方也回了数据,但是onCharacteristicChanged信号照样没有被触发,因此也收不到数据,连接蓝牙的过程中看到有如下信息打印,也是搞不清为什么。Qt论坛上看到之前有人提过相同的问题,但是没人解答。