在 gradle 3.0 之后 compile、provided、apk 被 deprecated,取而代之的是 implementation 、api、compileOnly、runtimeOnly。

本文重点是介绍implementation和api。

api

api 修饰的依赖会被暴露并传递给外部,也就是说:在外部module引用该lib时,module会把该lib的api依赖传递依赖过来,并添加到module自己的compile classpath上。(在运行期和编译期api依赖都可见。)如果有一个api dependency修改了对外API,那么所有依赖和间接依赖了该 dependency 的module都会被重新编译。

implementation

implementation 则相反,implementation修饰的依赖不会被暴露给外部module,在引用该lib时,lib内通过implementation申明的依赖不会被添加到module 的 compile classpath上。(也就是说lib的依赖仅在runtime可见,编译期不可见。)如果有一个implementation dependency 修改了API,那么仅该dependency 和 直接依赖于它的module会被重新编译。

compile

compile 的行为和api一致,已经被标记为deprecated。

为什么要提供两种依赖函数呢?

  • 可以更好的控制 transitive dependency

  • compile classpath更精简,编译速度得到提升

  • implementation dependencies 发生改变不会导致全量地重新编译

  • 在POM中依赖关系更加清晰:编译library的依赖和运行library的依赖区可以分开了。

举例说明:

└── App
   ├── implementation A
   │   └── implementation a1
   │   └── api a2
   └── implementation B
      └── implementation b1
  └── api b2

A lib分别通过implementation 和 api 依赖了a1、a2;
B lib分别通过implementation 和 api 依赖了b1、b2;
App 通过implementation直接依赖了A、B lib;
App 被A传递依赖了a2;
App 被B传递依赖了b2;

  1. 编译App时,compile classpath中的依赖有 A、B、a2、b2
  2. a1 对外暴露的API有修改 —— 仅 a1 和 A 会被重新编译,b1 同理。
  3. a2 对外暴露的API有修改 —— a2 、 A 、App 都会被重新编译,b2 同理。

如何判断自己library的依赖属于哪种类型呢?

api 依赖一般是指那些暴露在library binary interface(也叫Application Binary Interface)中的,包括但不仅仅限于下面case:

  • 在interface和父类中被引用到的

  • public方法参数中引用到的,包括泛型参数

  • public属性中引用到的

  • public annotation 中引用到的。

注这里 public 是广义上的,泛指对编译器可见,比如:publicprotected 以及 package private概念都是public的)

相反,下面case和ABI不相关的,则应该被申明为implementation dependency:

  • 仅仅被方法内部引用的

  • 仅仅被 private 成员引用的

  • 仅仅被内部类引用的(未来,Gradle 会允许用户申明那些包属于公共API)

官方提供了一个例子,如何根据上述规则推导不同依赖类型。

实际上使用时,可以偷懒地判断,大部分情况下:

  1. 在app和test module中应该使用implementation引入dependency
  2. 在library module中应该使用api引入dependency,除了非public引用的dependency。