如何做一个Android版的cocoapods?

3,564 阅读3分钟

这一篇我们先看下如何定义依赖描述。

如果我们平常经常维护主工程的基础仓库,经常会遇到修改仓库后无法很好的验证修改的效果,要么是把仓库工程作为单独的git submodule嵌入到业务工程,或者是简单粗暴,直接把线上依赖注释掉,添加本地仓库的路径。

那么有没有一种好的方法,可以做到本地源码依赖和线上依赖无缝切换。

了解过iOS开发的都知道,iOS依赖是直接下载依赖仓库的源码,本地依赖直接编译,依赖管理器叫CocoaPods,Android 平台是通过Gradle 从 Maven 或者JCenter 下载二进制文件。那在Android 平台上如何实现依赖仓库源码文件?

依赖管理器大概由几部分组成,依赖描述文件、发布规范等。依赖描述文件各个依赖管理器采用的格式都有所不同,Json 、 XML 、DSL 等等。各种表达方式各有各的优点,Json XML 结构工程表达比较丰富,但是可读性较差,当工程有几十个依赖项Json XML 简直无法直视,DSL虽然在工程结构表达上较弱,但在可读性理解上优点非常明显,cocoapods 就采用了这种表达方式,非常优美,如下。

platform :ios, '8.0'
use_frameworks!

target 'MyApp' do
  pod 'AFNetworking', '~> 2.6'
  pod 'ORStackView', '~> 3.0'
  pod 'SwiftyJSON', '~> 2.3'
end

看着这种依赖关系描述,心情也会舒畅好多。

那么在Android 平台能不能实现类似的效果?Android 构建使用的是groovy语言,而且groovy对DSL支持的还不错,所以利用groovy DSL特性描述依赖关系就可以了,你以为它是一段描述,其实它是一个可执行脚本。

我们需要一个模块的描述,包括开关、名字、路径等等,类似如下:

module {
    on_off true
    name "我是名字"
    path "/path"
}

ok,有了定义怎么实现?

我们看上面的依赖描述,其实都是一个个函数调用,比如 module 就可以是一个函数,函数的参数是一个闭包,同理on_off、name等等也是函数调用,在函数后面的即为函数的参数。

是不是很有意思?一句话就可以作为一个脚本执行。

所以,我们需要定义我们依赖描述中要用到的函数,如下:

class Module {
    boolean on_off
    String name
    String path

    void on_off(boolean on_off) {
        this.on_off = on_off
        println("module on_off:" + on_off)
    }

    void name(String name) {
        this.name = name
        println("module name:" + name)
    }

    void path(String path) {
        this.path = path
        println("module path:" + path)
    }
}

Module module(@DelegatesTo(strategy = Closure.DELEGATE_FIRST, value = Module) Closure script) {
    script.resolveStrategy = Closure.DELEGATE_FIRST
    Module module = new Module()
    script.delegate = module
    script()
    return script.delegate
}

名为module的函数接收一个闭包,闭包代理为Module类,闭包内的函数自动执行代理类函数调用,类和函数定义完成后,然后在控制台试验下(Android Studio -> Tools -> Groovy Console),在定义的类和函数下面写上如下的DSL生命,执行。

module {
    on_off true
    name "我是名字"
    path "我是路径"
}

结果如下:
module on_off:true
module name:我是名字
module path:我是路径

看来我们定义的DSL已经成功被解析。

依赖描述已经定义完成,下一篇会说下如何动态生成依赖描述文件。

仓库源码依赖管理器(二)