在Android开发中如果需要Http,Java层有大名鼎鼎的okHttp库。Java语言也有HttpURLConnection可以直接使用。但是在用C/C++开发native层代码时,如果你需要使用Http,你的头可能会疼一会儿,因为没有现成的接口可用。
好消息是有个强大的库curl,支持HTTP、HTTPS、FTP、FTPS...。功能太过强大,反正我现在只需要HTTP和HTTPS。它提供命令行工具,但我们同样可以通过C/C++调用接口。
要在Android里使用curl,首先得编译它。本文记录在mac系统使用Android NDK编译curl的过程。linux系统应该可以直接复用,Windows系统需要安装cygwin或mingw,Windows安装这个就是要一个shell的执行环境。推荐在mac或linux中编译。
先下载下curl源码,我没有使用github上面的,而是直接在官网下载,这样下载的源码比较干净,没有带那些git版本管理的文件。
如果想让curl支持https,需要依赖openssl。如果要支持压缩传输,需要依赖zlib库。同样,这两个库也没有使用github上面的源码,而是从官网下载。
openssl使用3.0.7(https://www.openssl.org/source/old/3.0/openssl-3.0.7.tar.gz)
zlib使用使用1.2.13(https://www.zlib.net/zlib-1.2.13.tar.gz)
curl使用7.88.0(https://curl.se/download/curl-7.88.0.tar.xz)
Android NDK使用了23.1.7779620。
开源软件的编译就那么几个套路。
1.先执行configure,configure完成后会生成Makefile,然后执行make编译,编译完成后执行make install安装。
2.有的开源软件源码没有configure,需要先autoconf生成configure,然后再执行第一步。
1.编译环境
这个环境是这几个软件都需要用的,我把它写到一个shell文件里面了。也可以在编译时单独配置一下。
build_env.sh
#!/bin/bash
#NDK路径,openssl需要ANDROID_NDK_ROOT变量,所以把它export一下
export ANDROID_NDK_ROOT=$HOME/Library/Android/sdk/ndk/23.1.7779620
#编译平台,我这里是mac,所以是darwin-x86_64
HOST_TAG=darwin-x86_64
#Android api版本
MIN_SDK_VERSION=23
#工具链路径
TOOLCHAIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG
#把工具链加到PATH环境变量
PATH=$TOOLCHAIN/bin:$PATH
#输出目录,在build目录下
BUILD_DIR=$PWD/build
2.编译zlib
解压zlib,并cd到zlib目录下
tar xvf zlib-1.2.13.tar.gz
cd zlib-1.2.13
配置编译工具链,就是配置C/C++编译器、汇编器、链接器的路径,由于Android已经放弃了gcc,现在的编译器是clang和clang++。
要适用于所有CPU架构,我们要编译armv8、armv7、x86、x86_64四个平台。他们会使用不同的clang来编译。我们用TARGET_HOST来区分。
aarch64-linux-android表示要编译armv8
armv7a-linux-androideabi表示要编译armv7a
i686-linux-android表示要编译x86
x86_64-linux-android表示要编译x86_64
TARGET_HOST=aarch64-linux-android
ANDROID_ARCH=arm64-v8a
AR=$TOOLCHAIN/bin/llvm-ar
CC=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang
AS=$CC
CXX=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang++
LD=$TOOLCHAIN/bin/ld
RANLIB=$TOOLCHAIN/bin/llvm-ranlib
STRIP=$TOOLCHAIN/bin/llvm-strip
配置好工具链后执行configure。--prefix指定编译完成后软件的安装目录。--static只编译静态库。
INSTALL_DIR=$BUILD_DIR/zlib
./configure --prefix=$INSTALL_DIR/$ANDROID_ARCH --static
#上面设置了BUILD_DIR为根目录build文件夹,所以$INSTALL_DIR/$ANDROID_ARCH的值为 build/zlib/arm64-v8a
再执行make、make install。
make
make install
install后,在根目录build/curl下,出现了arm64-v8a。里面就是arm64-v8a版本的zlib库。
我把四个架构的编译写成shell文件。
build-zlib.sh
#!/bin/bash
tar xzf zlib-1.2.13.tar.gz
source ./build-env.sh
INSTALL_DIR=$BUILD_DIR/zlib
if [ ! -d $INSTALL_DIR ]; then
mkdir -p $INSTALL_DIR
fi
cd zlib-1.2.13
function build() {
make distclean
TARGET_HOST=$1
ANDROID_ARCH=$2
AR=$TOOLCHAIN/bin/llvm-ar
CC=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang
AS=$CC
CXX=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang++
LD=$TOOLCHAIN/bin/ld
RANLIB=$TOOLCHAIN/bin/llvm-ranlib
STRIP=$TOOLCHAIN/bin/llvm-strip
./configure --prefix=$INSTALL_DIR/$ANDROID_ARCH --static
make -j8
make install
make distclean
}
build aarch64-linux-android arm64-v8a
build armv7a-linux-androideabi armeabi-v7a
build i686-linux-android x86
build x86_64-linux-android x86_64
cd ..
rm -rf cd zlib-1.2.13
3.编译openssl
通过上面的zlib编译,openssl的编译流程大致是一样的,解压代码什么的就没必要记录了。
有点不一样的是openssl的Configure是大写的。并且它自己支持了Android,需要通过参数传递给它。
android-arm64表示编译64位的arm版本。
no-unit-test表示不需要单元测试。
no-shared表示不需要动态库。
-D__ANDROID_API__=$MIN_SDK_VERSION传递Android api版本。
--prefix指定Android目录
./Configure android-arm64 no-unit-test no-shared -D__ANDROID_API__=$MIN_SDK_VERSION --prefix=$INSTALL_DIR/$ANDROID_ARCH
同样写成shell文件
build-openssl.sh
#!/bin/bash
tar xzf openssl-3.0.7.tar.gz
source ./build-env.sh
INSTALL_DIR=$BUILD_DIR/openssl
if [ ! -d $INSTALL_DIR ]; then
mkdir -p $INSTALL_DIR
fi
cd openssl-3.0.7
function build() {
TARGET_HOST=$1
ANDROID_ARCH=$2
OPENSSL_ARCH=$3
AR=$TOOLCHAIN/bin/llvm-ar
CC=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang
AS=$CC
CXX=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang++
LD=$TOOLCHAIN/bin/ld
RANLIB=$TOOLCHAIN/bin/llvm-ranlib
STRIP=$TOOLCHAIN/bin/llvm-strip
./Configure $OPENSSL_ARCH no-unit-test no-shared -D__ANDROID_API__=$MIN_SDK_VERSION --prefix=$INSTALL_DIR/$ANDROID_ARCH
make -j8
make install_sw
make distclean
}
build aarch64-linux-android arm64-v8a android-arm64
build armv7a-linux-androideabi armeabi-v7a android-arm
build i686-linux-android x86 android-x86
build x86_64-linux-android x86_64 android-x86_64
cd ..
rm -rf openssl-3.0.7
4.编译curl
准备完zlib和openssl之后,就可以编译curl了。同样是老套路,configure,make,make install。
--host和--target把编译器平台传给它。
--prefix依旧还是安装目录。
--with-zlib和--with-openssl就用到了我们上面编译出来的zlib和openssl。我们把目录传给它。
--disable-shared表示不编译动态库,我只需要静态库。
./configure --host=$TARGET_HOST \
--target=$TARGET_HOST \
--prefix=$INSTALL_DIR/$ANDROID_ARCH \
--with-zlib=$BUILD_DIR/zlib/$ANDROID_ARCH \
--with-openssl=$BUILD_DIR/openssl/$ANDROID_ARCH \
--disable-shared
curl的完整编译文件如下
build-curl.sh
#!/bin/bash
tar xzf curl-7.88.0.tar.xz
source ./build-env.sh
INSTALL_DIR=$BUILD_DIR/curl
if [ ! -d $INSTALL_DIR ]; then
mkdir -p $INSTALL_DIR
fi
cd curl-7.88.0
function build() {
export TARGET_HOST=$1
export ANDROID_ARCH=$2
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang
export AS=$CC
export CXX=$TOOLCHAIN/bin/$TARGET_HOST$MIN_SDK_VERSION-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
./configure --host=$TARGET_HOST \
--target=$TARGET_HOST \
--prefix=$INSTALL_DIR/$ANDROID_ARCH \
--with-zlib=$BUILD_DIR/zlib/$ANDROID_ARCH \
--with-openssl=$BUILD_DIR/openssl/$ANDROID_ARCH \
--with-pic --disable-shared
make -j8
make install
make clean
}
build aarch64-linux-android arm64-v8a
build armv7a-linux-androideabi armeabi-v7a
build i686-linux-android x86
build x86_64-linux-android x86_64
cd ..
rm -rf cd curl-7.88.0
5.大功告成
编译完后,所有的库就在build目录下了。我把build目录上传github,需要的话可以直接使用。