欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

用NDK生成cURL和OpenSSL库

程序员文章站 2022-03-07 10:33:12
最近在用Qt开发Android应用时需要获取https页面内容,但Qt内置的QNetworkAccessManager类只支持下面这些协议(调用其supportedSchemes成员函数获取): ("ftp", "file", "qrc", "http", "data") 而网上我找到的支持http ......

最近在用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。

注意:

  1. 如果系统没有gnu binutils,运行curl的configure脚本前需要把ndk的路径添加到path,而且是添加对应生成目标的路径,例如:path=“$android_ndk_home/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/”:$path,然后在生成不同目标平台库的时候先切换。
  2. qt应用项目的android系统版本号要大于等于生成curl和openssl设置的__android_api__版本号,否则链接时可能会出现找不到stdin、stdout和stderr外部符号的错误,一般生成库都是选个低点的系统版本兼容性更好。