什么是 ABI?

416 阅读3分钟

什么是 ABI?

库的 ABI 或库的应用程序二进制接口是消费者编译的对象(而不是您编码的对象!)。我喜欢将其视为库的二进制 API。了解 ABI 是启用 Gradle 功能的基础,例如启动增量编译和避免编译。可以将库的 ABI 视为其公共接口和针对它们进行编译所需的依赖项的集合:公共方法及其返回和参数类型,以及公共字段/属性。要了解这与依赖性分析有何关系,请考虑以下事项:

fun newOkHttpClient(gson: Gson): OkHttpClient { … }

这是一个以 Gson 实例为参数并返回 OkHttpClient 实例的公共函数。这意味着 Gson 和 OkHttp 都是 ABI 的一部分!在 Gradle 术语中,这意味着在构建脚本中需要以下内容:

dependencies {
  api "com.google.code.gson:gson:2.8.6"
  api "com.squareup.okhttp3:okhttp:4.8.0"
}

如果你使用 implementation 而不是 api,你的项目仍然可以编译,但你会给下游消费者带来麻烦。如果它们的编译类路径中没有这些库,它们将无法编译。库的作者正确地做到这一点至关重要。

API 和实现分离

标准 Java 插件和 Java 库插件之间的主要区别在于后者引入了向消费者公开的 API 的概念。库是一个 Java 组件,旨在供其他组件使用。

插件公开了两个可用于声明依赖项的配置:api 和 implementation。 api 配置应该用于声明库 API 导出的依赖项,而 implementation 配置应该用于声明组件内部的依赖项。

出现在 api 配置中的依赖项将传递给库的使用者,因此将出现在使用者的编译类路径中。另一方面,在实现配置中找到的依赖项不会暴露给消费者,因此不会泄漏到消费者的编译类路径中。这有几个好处:

  • 依赖项不再泄漏到消费者的编译类路径中,因此您永远不会意外依赖传递依赖项
  • 由于类路径大小减小,编译速度更快
  • 实现依赖项发生变化时减少重新编译:消费者不需要重新编译
  • 更清洁的发布:当与新的 maven-publish 插件一起使用时,Java 库会生成 POM 文件,这些文件可以准确区分针对库编译所需的内容和在运行时使用库所需的内容(换句话说,不要混合编译库本身所需的内容和针对库编译所需的内容)。

Gradle 7.0 中删除了compile和runtime配置。请参阅升级指南如何迁移到implementation和 api 配置。

[  {    "identifier": ":db",    "configurationName": "implementation"  },  {    "identifier": "androidx.appcompat:appcompat",    "resolvedVersion": "1.1.0-rc01",    "configurationName": "implementation"  }]

(以上表明项目 :db 和外部依赖项 androidx.appcompat:appcompat 都是该项目 ABI 的一部分,并且当前在“implementation”中被错误地声明。)

public abstract class com/seattleshelter/core/base/BaseActivity : androidx/appcompat/app/AppCompatActivity {
  public fun <init> ()V
  protected fun onCreate (Landroid/os/Bundle;)V
}

(上面表示被分析项目的公共 BaseActivity 类扩展了 AppCompatActivity 并有一个函数 onCreate,它以 Bundle 作为参数。)

案例分析

image.png

public final class com/hello/MyService {
    public final fun disableService (Landroid/content/Context;)Lio/reactivex/Completable;

这里我们有一个公共类,它有一个返回 Completable 的公共函数。因此 RxJava2 是这个项目的 ABI 的一部分。

public final class com/hello/dao/DbOp {
    public fun <init> (Landroid/content/Context;Ldagger/Lazy;)V

这是一个公共类,其公共构造函数以 Dagger.Lazy 作为参数。所以,Dagger 也是 ABI 的一部分。

这是在另一个项目中使用此草稿功能的示例输出:

$ ./gradlew db:reasonDebug --id io.reactivex.rxjava2:rxjava
You asked about the dependency io.reactivex.rxjava2:rxjava. You have been advised to add this dependency.

Shortest path to io.reactivex.rxjava2:rxjava from the current project:
:db
--- androidx.room:room-rxjava2
     --- io.reactivex.rxjava2:rxjava

Dependency io.reactivex.rxjava2:rxjava provides the following:
- 1651 classes
- 141 public constants

And this project exposes the following classes provided by this dependency:
- io.reactivex.Flowable

Please see abi-dump.txt for more information.