最近在用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项目之前,先写个控制台程序测试一下:

 #include <iostream>
#include <map>
#include <string>
#include <curl/curl.h> using namespace std; #define URL u8R"(https://raw.githubusercontent.com/LCTT/LFS-BOOK/9.0-translating/README.md)" typedef std::map<std::string, std::string> HeaderFields;
typedef struct {
int code;
std::string body;
HeaderFields headers;
} Response; size_t write_callback( void *data, size_t size, size_t nmemb, void *userdata )
{
Response *r = reinterpret_cast< Response * >( userdata );
r->body.append( reinterpret_cast< char * >( data ), size * nmemb );
return size * nmemb;
} int _cURL()
{
if( curl_global_init( CURL_GLOBAL_ALL ) != CURLE_OK )
{
cout << "Call curl_global_init failed! \n";
return ;
}// if CURL *curlHandle = curl_easy_init();
CURLcode res = CURLE_OK;
curl_slist *headerList = nullptr;
Response ret = {};
curl_easy_setopt( curlHandle, CURLOPT_URL, URL );
curl_easy_setopt( curlHandle, CURLOPT_WRITEFUNCTION, write_callback );
curl_easy_setopt( curlHandle, CURLOPT_WRITEDATA, &ret );
curl_easy_setopt( curlHandle, CURLOPT_SSL_VERIFYPEER, );
curl_easy_setopt( curlHandle, CURLOPT_SSL_VERIFYHOST, );
res = curl_easy_perform( curlHandle );
if( res != CURLE_OK )
cout << "The result code is: " << ( int )res << "\n";
if( false == ret.body.empty() )
cout << ret.body << "\n";
curl_easy_cleanup( curlHandle );
curl_global_cleanup(); return ;
} int main()
{
return _cURL();
}

保存为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,而且是添加对应生成目标的路径,例如要生成64位arm指令集:
PATH=“$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/”:$PATH

然后在生成不同目标平台库的时候先切换,例如通用arm指令集:

PATH=“$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/arm-linux-androideabi/bin/”:$PATH
  • Qt应用项目的Android系统版本号要大于等于生成cURL和OpenSSL设置的__ANDROID_API__版本号,否则链接时可能会出现找不到stdin、stdout和stderr外部符号的错误,一般生成库都是选个低点的系统版本兼容性更好。

用NDK生成cURL和OpenSSL库的更多相关文章

  1. Android NDK生成及连接静态库与动态库

    对于Android应用开发,大部分情况下我们使用Java就能完整地实现一个应用.但是在某些情况下,我们需要借助C/C++来写JNI本地代码.比如,在使用跨平台的第三方库的时候:为了提升密集计算性能的时 ...

  2. C++调用openssl库生成RSA加密秘钥对

    直接上代码.默认生成的是pkcs#1格式 // ---- rsa非对称加解密 ---- // #define KEY_LENGTH 1024 // 密钥长度 #define PUB_KEY_FILE ...

  3. Android NDK生成共享库和静态库

    Date: 2014-03-14 Title: Compile Android Native Binary And Library Published: true Type: post Tags: A ...

  4. NDK jni 加载静态库

    加载静态库到android,静态库的提供方式有2种, a. 通过源文件来编译静态库 b. 加载已经编译好的静态库 首先我们来看,通过源文件来编译静态库,工程目录如下 第一步:我们来看我们的jni目录, ...

  5. android开发 NDK 编译和使用静态库、动态库 (转)

    在eclipse工程目录下建立一个jni的文件夹 在jni文件夹中建立Android.mk和Application.mk文件 Android.mk文件: Android提供的一种makefile文件, ...

  6. 使用openssl库实现RSA、AES数据加密

         openssl是可以很方便加密解密的库,可以使用它来对需要在网络中传输的数据加密.可以使用非对称加密:公钥加密,私钥解密.openssl提供了对RSA的支持,但RSA存在计算效率低的问题,所 ...

  7. vs2010编译curl为static库及测试

    1,编译curl为static库 用vs2010打开: curl-7.32.0\vs\vc6\vc6curl.dsw 选择LIB Release生成libcurl静态库: curl-7.32.0\vs ...

  8. php开启curl和openssl

    php开启curl和openssl 开启php curl函数库的步骤 1).去掉windows/php.ini 文件里;extension=php_curl.dll前面的; /*用 echo phpi ...

  9. [转]使用openssl库实现RSA、AES数据加密

    openssl是可以很方便加密解密的库,可以使用它来对需要在网络中传输的数据加密.可以使用非对称加密:公钥加密,私钥解密.openssl提供了对RSA的支持,但RSA存在计算效率低的问题,所以一般的做 ...

随机推荐

  1. JVM参数笔记

    Java启动参数共分为三类: 其一是标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容: 其二是非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足, ...

  2. 如果要对一些数据做处理,可以直接用自定义fliter

    {{[offer.brand, offer.series, offer.model] | carFilter}} filters里面写 carFilter ([brand, series, model ...

  3. ubuntu笔记1-vim安装报错

    ubuntu安装vim的时候,报错提示:vim : 依赖: vim-common (= 2:7.3.429-2ubuntu2) 但是 2:7.3.429-2ubuntu2.1 正要被安装 说明既存的v ...

  4. ssh免密钥登陆的两种方式

    ssh 免密钥登陆的两种方式第一种:直接使用命令复制过去ssh-copy-id root@192.168.3.113批量复制for i in {113..140}; do ssh-copy-id ro ...

  5. JavaScript初探系列(五)——this指向

    一.涵义 this关键字是一个非常重要的语法点.毫不夸张地说,不理解它的含义,大部分开发任务都无法完成.this可以用在构造函数之中,表示实例对象.除此之外,this还可以用在别的场合.但不管是什么场 ...

  6. 第08组 Alpha冲刺(2/4)

    队名 八组评分了吗 组长博客 小李的博客 作业博客 作业链接 组员1李昕晖(组长) 过去两天完成了哪些任务 文字/口头描述 11月17日了解各个小组的进度与难以攻破的地方,与隔壁第七组组长讨论进度发展 ...

  7. Java finally未被执行的情况

    一种是先执行了用于终止程序的System.exit()方法,或进程被关闭 还有一种情况是,当前线程一直在执行,在一些业务逻辑里面跳不出来,看上去就像finally一直未被执行 线程被终止的时候也会执行 ...

  8. PV、TPS、QPS是怎么计算出来的?(转载的)

    QPS = req/sec = 请求数/秒 [QPS计算PV和机器的方式] QPS统计方式 [一般使用 http_load 进行统计] QPS = 总请求数 / ( 进程总数 * 请求时间 ) QPS ...

  9. java中为什么notify()可能会导致死锁,而notifyAll()则不会

    简单的说,notify()只唤醒一个正在等待的线程,当该线程执行完以后施放该对象的锁,而没有再次执行notify()方法,则其它正在等待的线程 则一直处于等待状态,不会被唤醒而进入该对象的锁的竞争池, ...

  10. 014-交互式Shell和shell脚本获取进程 pid

    Linux 的交互式 Shell 与 Shell 脚本存在一定的差异,主要是由于后者存在一个独立的运行进程 1.交互式 Bash Shell 获取进程 pid 在已知进程名(name)的前提下,交互式 ...