Maria 开发记录 II

OS X Nov 4, 2016

Maria Logo

Maria 是为 aria2 这款命令行下载软件定制的 Native App,开发 Maria 本身是不用写下载核心代码的。

这个项目最初创建于4月份,至今已经有6个月了,从最初的一个 Today Widget 发展到现在已经快要接近于完整的 macOS 应用了,(但是在我眼里还是不算完整应用,因为下载核心需要依赖 aria2 项目,而缺乏自己的下载核心程序),之前也写过一篇文章简单讲了一下这个程序的编写过程。最近的几次更新主要是对 aria2 的深度集成,到目前也已经有所成果了,所以准备记录一下这其中的过程和坑。

集成 aria2

一开始是根本没打算集成 aria2 的,因为考虑到其他类似的软件比如 Aria2GUI 或者是 Aria2D 都是用的直接打包一个 aria2 二进制包的方式,实际上还是通过 rpc 的方式来控制下载操作。

我个人觉得这种方式集成 aria2 不如不集成,就现在这样依赖外部运行更好,相互之间独立,aria2 、Maria 和 webui-aria2 能够更好的搭配工作。

后来再仔细翻阅了 aria2 的文档,发现了 libaria2 这个东西的存在,才把 Maria 集成 aria2 的计划真正的实施起来。

aria2 是由 C++ 11 编写的,而 Maria 是用 Swift 编写的,如果想要 Maria 直接调用 aria2 的内部接口,还需要一个连接桥梁,也就是 Objective-C,简单来说,就是需要写一个 OC++ 的 wrapper 来向 Swift 引入 aria2 的 C++ 接口。

获取 libaria2.dylib

现在 aria2 静态库文件 libaria2-1.28.0-maria.dylibaria2.h 已经从 git 中移除,你需要下载 aria2 源码自行编译生成:

(需要注意的是:macOS 可能会缺失一些工具包,请根据编译信息自行通过 Homebrew 安装)

# 在编译之前你需要先安装一些依赖包
brew install autoconf
brew install automake
brew install libtool

git clone https://github.com/ShinCurry/aria2
cd aria2
git checkout lib-for-maria 

export PATH="$PATH:/usr/local/opt/gettext/bin"
autoreconf -i
./configure --enable-libaria2
sudo make
sudo make install

如果编译安装成功,你应该能在 /usr/local/lib//usr/local/include/aria2/ 目录下找到相应的文件。然后将相应文件拷贝到 Maria 工程子目录 /Aria2Core 下:

cd path/to/your/projectdir/Aria2Core/

cp /usr/local/lib/libaria2.0.dylib ./Frameworks/libaria2-1.28.0-maria.dylib
install_name_tool -id "@loader_path/Frameworks/libaria2-1.28.0-maria.dylib" ./Frameworks/libaria2-1.28.0-maria.dylib

cp /usr/local/include/aria2/aria2.h ./

拿到静态库相关文件,接下来就需要写 wrapper 了。

我的做法是再新建一个 Objective-C 的 Framework Project 再引入到 Maria 中,这样和之前写好的 Aria2RPC Framework 就更好的统一了。

Objective-C++ for C++ 的 wrapper 不难写,只需要注意其中的一些数据类型的转换,比如:

  • C++ 的结构体(Struct)全部被转换成了 Objective-C++ 的类(Class)
  • C++ 中作为每个下载任务 ID 的 uint64_t A2GID 类型被转换成了 Objective-C++ 的 NSString
  • C++ 中作为 aria2 配置选项的 std::vector<std::pair<std::string, std::string>> 被转换成了 Objective-C++ 的 NSDictionary<NSString *, NSString *>

另外还有一大堆别名定义,为了更方便的进行数据转换,直接在名字前加上 AC(Aria2Core) 的前缀:

// Public
typedef NSDictionary<NSString *, NSString *> ACKeyVals;
typedef NSString ACUri;
typedef NSArray<NSString *> ACUris;
typedef NSString ACGid;
typedef NSArray<NSString *> ACGids;
typedef NSArray<ACUriData *> ACUriDatas;

// Private
typedef std::vector<std::string> Uris;
typedef uint64_t Gid;
typedef std::vector<uint64_t> Gids;
typedef aria2::KeyVals KeyVals;
typedef aria2::OffsetMode OffsetMode;
typedef aria2::BtFileMode BtFileMode;
typedef aria2::UriData UriData;
typedef std::vector<UriData> UriDatas;
typedef aria2::FileData FileData;

值得注意的是,创建的虽然是一个 Objective-C++ Framework Project,不过在 Maria 这个项目里面需要通过桥接文件在 Swift 里调用,所以,OC Framework 对外暴露的方法和类型定义不能有任何 C++ 的东西出现,不然使用 Swift 编译器编译的时候就会出现找不过各种头文件的错误。

另外还有一个待解决的问题就是,dealloc() 函数再程序退出的时候并没有调用。

对 aria2rpc framework 的修改

以前是直接在 framework 里面写成了单例运行模式,现在应该有了 aria2core 的存在,所以为了统一,改回了普通的类定义方式

Maria 调用 aria2 相关

最近的版本,新增了一个 Manager aria.swift 用来统一调用 aria2 相关的动态库,大致结构如下:

class Aria {
    var rpc: Aria2!
    var core: Aria2Core?
    
    static let shared = Aria()
    
    private init() {
        initRPC()
        initCore()
    }
}

在这个 Manager 中使用单例模式,并将所有 aria2 的初始化代码全部移动到这里面。

接下来需要完成的工作

是否完全使用 libaria2 提供的内部接口,去除 rpc 接口还在考虑当中。

Tags

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.