构建 Aria2 (Android share library)

本文详细介绍了如何在Android平台上构建Aria2下载工具,包括安装系统工具、下载并配置NDK、构建依赖库如c-ares、libexpat、openssl等,以及通过JNI对接和Log对接实现Aria2在APP中的二次开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

搭建工具环境

安装系统工具

sudo apt-get install autoconf ???point docbook2x tofrodos

下载NDK,android-ndk-r14b-linux-x86_64.zip,解压后,在NDK目录执行:

mkdir -p /mnt/work/toolchain/android-standalone-r14b
export ANDROID_HOME=/mnt/work/toolchain/android-standalone-r14b

build/tools/make_standalone_toolchain.py \
   --arch arm --api 16 --stl=gnustl \
   --install-dir $ANDROID_HOME/toolchain

export ANDROID_NDK_ROOT=/mnt/work/toolchain/ndk/android-ndk-r14b
export ANDROID_NDK_HOME=/mnt/work/toolchain/ndk/android-ndk-r14b

构建依赖库

c-ares

git clone https://github.com/c-ares/c-ares.git
cd c-ares
git br 1.15.0 e982924acee7f7313b4baa4ee5ec000c5e373c30
git co 1.15.0
./buildconf
./configure \
  --prefix=${ANDROID_HOME}/usr/local \
  --host=arm-linux-androideabi \
  CC=arm-linux-androideabi-gcc \
  --enable-shared=no \
  --enable-static=yes
make install
cd ..

libexpat

git clone https://github.com/libexpat/libexpat.git
cd libexpat
git br 2.2.6 39e487da353b20bb3a724311d179ba0fddffc65b
git co 2.2.6
./buildconf
./configure \
  --prefix=${ANDROID_HOME}/usr/local \
  --host=arm-linux-androideabi \
  CC=arm-linux-androideabi-gcc \
  --enable-shared=no \
  --enable-static=yes \
  --with-docbook
make install
cd ..

openssl

git clone https://github.com/openssl/openssl.git
cd openssl
git br 1.1.1b 50eaac9f3337667259de725451f201e784599687
git co 1.1.1b
wget https://wiki.openssl.org/images/7/70/Setenv-android.sh
fromdos Setenv-android.sh
chmod u+x Setenv-android.sh
. Setenv-android.sh
./config shared no-ssl2 no-ssl3 no-comp no-hw no-engine \
  --openssldir=${ANDROID_HOME}/usr/local \
  --prefix=${ANDROID_HOME}/usr/local
make depend
make all
make install \
  CC=$ANDROID_TOOLCHAIN/arm-linux-androideabi-gcc \
  RANLIB=$ANDROID_TOOLCHAIN/arm-linux-androideabi-ranlib
export PATH=${PATH#*:}
cd ..

zlib

(tar.gz包手动下载)

tar -xzvf zlib-1.2.11.tar.gz
cd zlib-1.2.11
export CC=arm-linux-androideabi-gcc
./configure \
  --prefix=${ANDROID_HOME}/usr/local \
  -static
make install

libssh2

git clone https://github.com/libssh2/libssh2.git
cd libssh2
# 1.8.2 failed need patch d071e0e07b2, use master
# git br 1.8.2 02ecf17a6d5f9837699e8fb3aad0c804caa67eeb
# git co 1.8.2
./configure \
  --prefix=${ANDROID_HOME}/usr/local \
  --host=arm-linux-androideabi \
  CC=arm-linux-androideabi-gcc \
  --enable-shared=no \
  --enable-static=yes
make install

构建aria2

git clone https://github.com/aria2/aria2.git
cd aria2
autoreconf -i
./android-config
./android-make
./android-release
ln -s src/.libs/libaria2.a
cd ..

如果要生成动态链接库

/bin/bash ../libtool --silent --tag=CXX --mode=link \
  /home/cmguo/work/toolchain/android-standalone-r14b/toolchain/bin/arm-linux-androideabi-clang++ \
  -export-dynamic -std=c++11 -pipe -Os -g -fPIE -pie -shared -o libaria2.so main.o libaria2.la \
  /home/cmguo/work/toolchain/android-standalone-r14b/usr/local/lib/libz.a \
  /home/cmguo/work/toolchain/android-standalone-r14b/usr/local/lib/libexpat.a \
  /home/cmguo/work/toolchain/android-standalone-r14b/usr/local/lib/libssl.a  \
  /home/cmguo/work/toolchain/android-standalone-r14b/usr/local/lib/libcrypto.a \
  /home/cmguo/work/toolchain/android-standalone-r14b/usr/local/lib/libssh2.a \
  /home/cmguo/work/toolchain/android-standalone-r14b/usr/local/lib/libcares.a \
  -llog ../deps/wslay/lib/libwslay.la

arm-linux-androideabi-strip -s libaria2.so -o ../libaria2.so

 二次开发

动态库生成后,我们就可以在APP里面加载aria2库了。

JNI对接

我们只使用rpc(即http)接口操作aria2,所以只要完成启动其main入口就行了。为此,我们将main方法通过JNI形式提供给java层调用。

extern "C" JNIEXPORT jint JNICALL Java_com_xxx_aria2_Aria2_main(JNIEnv *env, jclass clazz, jobjectArray jargv)
{    //jargv is a Java array of Java strings
    int argc = env->GetArrayLength(jargv);
    typedef char *pchar;
    pchar *argv = new pchar[argc];
    int i;
    for(i = 0; i < argc; i++)
    {
        jstring js = (jstring) env->GetObjectArrayElement(jargv, i); //A Java string
        jboolean isCopy;
        const char *pjc = env->GetStringUTFChars(js, &isCopy); //A pointer to a Java-managed char buffer
        size_t jslen = strlen(pjc);
        argv[i] = new char[jslen+1]; //Extra char for the terminating null
        strcpy(argv[i], pjc); //Copy to *our* buffer. We could omit that, but IMHO this is cleaner. Also, const correctness.
        env->ReleaseStringUTFChars(js, pjc);
    }

    //Call main
    chdir(argv[0]);
    int ret = main2(argc, argv);

    //Now free the array
    for(i=0;i<argc;i++)
        delete [] argv[i];
    delete [] argv;

    return ret;
}

Log对接

但启动出现问题时,通过aria2自己的输出日志排查问题是很必要的。Aria2通过OutputFile基类输出日志,因此我们可以很方便的扩展一个使用Android Log的OutputFile——LogcatFile。

// LogcatFile.h

#include "OutputFile.h"

#include <android/log.h>
#include <stdarg.h>

namespace aria2 {

class LogcatFile : public OutputFile {
public:
  LogcatFile(int l)
  {
     level_ = l;
  }
  virtual ~LogcatFile() = default;
  virtual size_t write(const char* str)
  {
     __android_log_print(level_, "Aria2", "%s", str);
     return 0;
  }

  virtual int flush() { return 0; }
  virtual int vprintf(const char* format, va_list va)
  {
     __android_log_vprint(level_, "Aria2", format, va);
     return 0;
  }
  inline int printf(const char* format, ...)
  {
    va_list va;
    va_start(va, format);
    int rv = vprintf(format, va);
    va_end(va);
    return rv;
  }
  virtual bool supportsColor() { return true; }
private:
  int level_;
};

}

在Console初始化中,增加Android情况下的选择策略,使用LogcatFile。

#include "NullOutputFile.h"
#ifdef __MINGW32__
#  include "WinConsoleFile.h"
#elif __ANDROID__
#  include "LogcatFile.h"
#else // !__MINGW32__
#  include "BufferedFile.h"
#endif // !__MINGW32__


void initConsole(bool suppress)
{
  if (suppress) {
    consoleCout = consoleCerr = std::make_shared<NullOutputFile>();
  }
  else {
#ifdef __MINGW32__
    consoleCout = std::make_shared<WinConsoleFile>(STD_OUTPUT_HANDLE);
    consoleCerr = std::make_shared<WinConsoleFile>(STD_ERROR_HANDLE);
#elif __ANDROID__
    consoleCout = std::make_shared<LogcatFile>(3);
    consoleCerr = std::make_shared<LogcatFile>(5);
#else  // !__MINGW32__
    consoleCout = std::make_shared<BufferedFile>(stdout);
    consoleCerr = std::make_shared<BufferedFile>(stderr);
#endif // !__MINGW32__
  }
}

Java封装

接下来,我们就可以在APP使用aria2了。

public class Aria2
{
    
    protected static final String TAG = "Aria2";

    static {
        System.loadLibrary("aria2");
    }
    
    private static Thread sThread;

    public static void start(Context context) {
        File extDir = context.getExternalCacheDir();
        File storageDir = new File(extDir, "aria2");
        File sessionFile = new File(extDir, "aria2.session");
        File cacheDir = context.getCacheDir();
        File confFile = new File(cacheDir, "aria2.conf");
        File logFile = new File(cacheDir, "aria2.log");
        
        storageDir.mkdirs();
        Assets.extract(context, "aria2.conf", confFile);
        try {
            new FileOutputStream(sessionFile).close();
        } catch (Exception e) {
        }
        final List<String> argv = new ArrayList<String>();
        argv.add(cacheDir.getAbsolutePath());
        argv.add("--conf-path=" + confFile.getAbsolutePath());
        argv.add("--log=" + logFile.getAbsolutePath());
        argv.add("--input-file=" + sessionFile.getAbsolutePath());
        argv.add("--save-session=" + sessionFile.getAbsolutePath());
        argv.add("--dir=" + storageDir.getAbsolutePath());
        sThread = new Thread(new Runnable() {
            @Override
            public void run() {
                main(argv.toArray(new String[argv.size()]));
            }
        });
        sThread.start();
    }
}

这里我们做了下面几件事:

  1. 在assets里面集成了配置文件 aria2.conf,一开始将其解压出来
  2. 通过命令行参数传递一些与文件路径有关的传输,比如conf-path、log、dir等,有些目录和文件要提前准备好
  3. 配置文件 aria2.conf的deamon改成false,要不然aria2会fork一个新进程,其实是fork了一个应用进程,这会导致一些不可预见的问题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fighting Horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值