在android开发过程中,无论是java层或者native层,我们经常会写一些关键日志信息到文件中,方便定位问题,可能存在将日志文件进行上传到服务器上,或者需要从服务器上下载一些配置信息文件,当然我们可以通过java层的http请求进行上传或者下载操作,但是其实为了更好的跨平台特性,我们可以使用libcurl来实现。
简介
libcurl为一个免费开源的,客户端url传输库,支持DICT, FILE, FTP, FTPS, Gopher, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, Telnet and TFTP等协议。
libcurl也支持SSL证书,HTTP POST, HTTP PUT,FTP上传,基于表单的HTTP上传,代理(proxies)、cookies、用户名/密码认证(Basic, Digest, NTLM等)、下载文件断点续传,上载文件断点续传(file transfer resume),http代理服务器管道(proxy tunneling)等。这通常我们需要与openssl库共同配合来完成加密请求。
libcurl是高度可移植的,可以工作在不同的平台上,支持Windows,Unix,Linux等。
libcurl是免费的,线程安全的,IPV6兼容的,同时它还有很多其他非常丰富的特性。libcurl已经被很多知名的大企业以及应用程序所采用。
总结起来:curl的特性就是开源、支持协议多、支持ssl、支持跨平台、支持ipv6等等诸多特性。
在Android平台的编译
环境准备
- ndk环境,ndk环境请自行下载
- curl源码(7.64.1),下载地址。
- 交叉编译工具链,请参考自定义NDK交叉编译工具类
编译
定义环境变量
export TOOLCHAIN=$ANDROID_HOME/toolchain // toolchain路径,自行修改
export PKG_CONFIG_LIBDIR=$TOOLCHAIN/lib/pkgconfig
export CROSS_SYSROOT=$TOOLCHAIN/sysroot
export PATH=$TOOLCHAIN/bin:$PATH
export TOOL=arm-linux-androideabi
export CC=$TOOLCHAIN/bin/${TOOL}-gcc
export CXX=$TOOLCHAIN/bin/${TOOL}-g++
export LINK=${CXX}
export LD=$TOOLCHAIN/bin/${TOOL}-ld
export AR=$TOOLCHAIN/bin/${TOOL}-ar
export RANLIB=$TOOLCHAIN/bin/${TOOL}-ranlib
export STRIP=$TOOLCHAIN/bin/${TOOL}-strip
export ARCH_FLAGS="-mthumb"
export ARCH_LINK=
export CFLAGS="${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64"
export CXXFLAGS="${CFLAGS} -frtti -fexceptions"
export LDFLAGS="${ARCH_LINK}"
编译并安装
cd curl-7.64.1
autoreconf -i
./configure --prefix=$TOOLCHAIN/sysroot/usr/local \
--with-sysroot=$TOOLCHAIN/sysroot \
--host=arm-linux-androideabi \
--with-ssl=$TOOLCHAIN/sysroot/usr/local \
--with-nghttp2=$TOOLCHAIN/sysroot/usr/local \
--enable-ipv6 \
--enable-static \
--enable-threaded-resolver \
--disable-dict \
--disable-gopher \
--disable-ldap --disable-ldaps \
--disable-manual \
--disable-pop3 --disable-smtp --disable-imap \
--disable-rtsp \
--disable-shared \
--disable-smb \
--disable-telnet \
--disable-verbose
make -j4
make install
./configure ...
过程自己根据当前项目的需求,disable不相关的配置,减少库体积。
生成库
编译完库,生成在./configure时指定的prefix路径下
将include和lib中的静态库引用到android工程中进行使用。
遇到问题
生成的库放在ndk中使用时,我们ndk如果APP_PLATFORM目标小于android-21
,也就是LOLLIPOP版本以下时,编译会报错:undefined reference to 'getpwuid_r'
,链接不到getpwuid_r
方法,其实是在android-21以下的编译环境中,没有该方法,如果要使用,必须将APP_PLATFORM提升到android-21
,对应的,我们的app minSdkVersion也必须要升到21。升级支持最低系统版本号这种做法,其实不是我们想要的。
没办法,只能去看curl源码,发现我们在使用到该方法的地方,都有用宏HAVE_GETPWUID_R
控制
#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
if((!username || !username[0]) &&
!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
pw_res) {
username = pw.pw_name;
}
#endif
那么只要在编译的时候,将该宏undefine,在编译和运行时就能跳过该方法,具体做法如下:
export CFLAGS="${ARCH_FLAGS} -fpic -ffunction-sections -funwind-tables -fstack-protector -fno-strict-aliasing -finline-limit=64 -DHAVE_GETPWUID_R=0"
或者在使用到该宏的源码处,取消宏定义
#undef HAVE_GETPWUID_R
这样curl重新编译,就能够在android-21以下的系统版本中使用啦...