最近在用Qt开发Android应用时需要获取https页面内容,但Qt内置的QNetworkAccessManager类只支持下面这些协议(调用其supportedSchemes成员函数获取):
("ftp", "file", "qrc", "http", "data")
而网上我找到的支持https的介绍是使用QSslConfiguration类,然后把OpenSSL的两个DLL(libeay32.dll和ssleay32.dll)复制到Qt库目录中,但我始终没成功,也就懒得在手机上折腾了。
这个思路不行,还有两个方案:一是通过QAndroidJniEnvironment和QAndroidJniObject调用Android SDK中封装的Https访问代码,还有就是通过cURL库,可以复用以前的代码,而且性能也不错,所以选择这个方案。
首先是准备工作:
- Windows(Win10 x64)
- 下载并安装msys2(http://repo.msys2.org/distrib/x86_64/),启动msys2_shell.cmd脚本,运行“pacman -Syuu”升级后关闭控制台窗口,重新启动后再运行一遍。这一步是可选的,如果不更新应该也可以,但我是更新后开始下一步的。
- 下载Windows版NDK并安装
- 设置ANDROID_NDK_HOME环境变量:exprot ANDROID_NDK_HOME=/d/android-ndk-r20
- 更新PATH环境变量:export PATH="/d/android-ndk-r20/toolchains/llvm/prebuilt/windows-x86_64/bin/":$PATH
- Linux(CentOS 8)
- 下载Linux版NDK并安装
- 设置ANDROID_NDK_HOME环境变量:exprot ANDROID_NDK_HOME=/home/user/android-ndk-r20
- 更新PATH环境变量:export PATH="/home/user/android-ndk-r20/toolchains/llvm/prebuilt/linux-x86_64/bin":$PATH
- 下载OpenSSL源码包,版本必须是1.1.x,开始我没注意,用的是之前下载的1.0.x,折腾了很长时间也没搞定,郁闷!
- 下载cURL源码包,我用的版本是7.66.0
然后就可以开工,下面列出的命令都是Linux平台,Windows里面msys2的命令大同小异。
生成OpenSSL
- cd openssl-1.1.1d
- ./Configure shared android-arm -D__ANDROID_API__=23 no-asm no-ssl2 no-ssl3 no-comp no-hw no-engine --prefix=/usr/local/ssl
- make -j && make install
上面第二行命令中的android-arm参数要注意,脚本提示可选的系统/编译器有很多:
android-arm android-arm64 android-armeabi android-mips android-mips64 android-x86 android-x86_64 android64 android64-aarch64 android64-mips64 android64-x86_64
但要选择哪个取决于你所用NDK根目录下“platforms/android-xx”里面的子目录能对应上才行,例如android-ndk-r20中的android-16里面只有arch-arm和arch-x86,所以如果命令行设置__ANDROID_API__=16的话,是不能生成android-arm64版库文件的。
接下来是cURL
- cd curl-7.66.0
- CC=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang ./configure --prefix=/usr/local/curl --host=arm-linux-androideabi --with-ssl=/usr/local/ssl/
- make -j && make install
在用到Qt项目之前,先写个控制台程序测试一下:
1 #include <iostream> 2 #include <map> 3 #include <string> 4 #include <curl/curl.h> 5 6 7 using namespace std; 8 9 10 #define URL u8R"(https://raw.githubusercontent.com/LCTT/LFS-BOOK/9.0-translating/README.md)" 11 12 13 typedef std::map<std::string, std::string> HeaderFields; 14 typedef struct { 15 int code; 16 std::string body; 17 HeaderFields headers; 18 } Response; 19 20 21 size_t write_callback( void *data, size_t size, size_t nmemb, void *userdata ) 22 { 23 Response *r = reinterpret_cast< Response * >( userdata ); 24 r->body.append( reinterpret_cast< char * >( data ), size * nmemb ); 25 return size * nmemb; 26 } 27 28 29 int _cURL() 30 { 31 if( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK ) 32 { 33 cout << "Call curl_global_init failed! \n"; 34 return 1; 35 }// if 36 37 CURL *curlHandle = curl_easy_init(); 38 CURLcode res = CURLE_OK; 39 curl_slist *headerList = nullptr; 40 Response ret = {}; 41 curl_easy_setopt( curlHandle, CURLOPT_URL, URL ); 42 curl_easy_setopt( curlHandle, CURLOPT_WRITEFUNCTION, write_callback ); 43 curl_easy_setopt( curlHandle, CURLOPT_WRITEDATA, &ret ); 44 curl_easy_setopt( curlHandle, CURLOPT_SSL_VERIFYPEER, 0 ); 45 curl_easy_setopt( curlHandle, CURLOPT_SSL_VERIFYHOST, 0 ); 46 res = curl_easy_perform( curlHandle ); 47 if( res != CURLE_OK ) 48 cout << "The result code is: " << ( int )res << "\n"; 49 if( false == ret.body.empty() ) 50 cout << ret.body << "\n"; 51 curl_easy_cleanup( curlHandle ); 52 curl_global_cleanup(); 53 54 return 0; 55 } 56 57 58 int main() 59 { 60 return _cURL(); 61 }
保存为test.cc,然后生成Android控制台程序:
$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang++ test.cc -I/usr/local/curl/include -Wl,-Bstatic -lcurl -lssl -lcrypto -Wl,-Bdynamic -o test
注意curl、ssl和crypto三个库用-Wl,-Bstatic方式指定链接静态库(顺序不能错),否则默认会链接动态库,但非root手机没有复制动态库到系统目录的权限,所以需要静态链接得到test,然后开启Android手机的调试模式并连接主机USB,最后在命令行切换到Android SDK的platform-tools目录:
- 把文件复制到手机存储器:adb push test /data/local/tmp
- 启动adb Shell:adb shell
- 文件添加可执行权限:chmod +x /data/local/tmp/test
- 启动:/data/local/tmp/test
顺利的话就可以正确获取并显示页面中的文本,然后就可以导入Qt项目,首先把cURL的头文件都复制到项目的目录中,然后把libcurl.a、libssl.a和libcrypto.a三个库文件复制到项目路径的android/lib中,修改*.pro文件,添加下面一行:
LIBS += -L$$PWD/android/lib -lcurl -lssl -lcrypto
如果已经有LIBS就在后面加上-lcurl -lssl -lcrypto三个库的引用,最后即可生成APK。
注意:
- 如果系统没有GNU binutils,运行cURL的configure脚本前需要把NDK的路径添加到PATH,而且是添加对应生成目标的路径,例如:PATH=“$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/”:$PATH,然后在生成不同目标平台库的时候先切换。
- Qt应用项目的Android系统版本号要大于等于生成cURL和OpenSSL设置的__ANDROID_API__版本号,否则链接时可能会出现找不到stdin、stdout和stderr外部符号的错误,一般生成库都是选个低点的系统版本兼容性更好。
原文地址:https://www.cnblogs.com/qiliang/p/11663058.html