【问题标题】:Gradle for Android, AARs, and Conditional DependenciesGradle for Android、AAR 和条件依赖
【发布时间】:2014-01-23 03:50:14
【问题描述】:

短格式:有哪些方法可以组织 AAR 的代码/POM,以便使用该 AAR 的应用程序只有它们实际需要的依赖项?

长格式

假设我们有一个依赖于一个打包为 AAR (L) 的 Android 库项目的应用。

L 包含多个类,任何给定的应用程序(如 A)只会使用这些类的子集。例如:

  • L 可能包含 Fragment 原生 API 级别 11 片段、反向移植片段和 ActionBarSherlock 风味片段的实现

  • L 可能包含用于常规活动的 Activity 实现、FragmentActivityActionBarActivity 和 ActionBarSherlock 风格的活动

  • L 可以通过LocalBroadcastManager、Square 的 Otto 和 greenrobot 的 EventBus 引发事件

  • 等等

在我看来,这些案例有两个主要的共同点:

  1. 应用程序通常只关心某些类。例如,使用 Otto 的应用不会关心引用 greenrobot 的 EventBus 的代码,或者使用 ActionBarActivity 的应用不会关心 ActionBarSherlock。

  2. 如果 AAR 是存储库中的工件,应用程序将不会关心构建 AAR 所需的所有可能的上游依赖项。例如,使用原生 API 级别 11 片段的应用将不需要 support-v4actionbarsherlock,即使 AAR 本身需要它们来构建 AAR

如果我们使用 JAR 而不是 AAR 并转储依赖项管理,这相当简单。构建 JAR 将具有编译时依赖项(例如,support-v4)。但是,使用该 JAR 的应用程序可以跳过这些依赖项,只要这些应用程序不使用真正需要这些依赖项的类,生活就很好。

但是,我很难看到如何使用 build.gradle 文件中指定的 AAR 和 Maven 工件来完成相同的事情。如果 L 有一个 dependencies 块引用上游依赖项,则当应用程序依赖 L 时,应用程序将依次下载这些依赖项。

我相当确定可行的一个解决方案是将 L 拆分为多个库。例如,使用片段场景,我们可以:

  • L1,其中包含原生 API 级别 11 版本片段的实现,以及其他场景所需的任何通用代码。该库将没有上游依赖项。

  • L2,其中包含使用 Android 支持包的片段反向移植的实现。 L2 将依赖于 L1 和 support-v4

  • L3,其中包含使用 Sherlock 风格片段的实现。 L3 将依赖于 L1 和 actionbarsherlock

然后,应用会选择是否依赖 L1、L2 或 L3,因此只会获得必要的上游依赖项(如果有的话)。

我的问题是:这是最好的解决方案吗?或者在 Gradle for Android、AAR 和 Maven 风格的工件的世界中是否有其他东西可以让应用程序依赖于单个 L?我担心库可能会组合爆炸以处理上游依赖项的各种组合。我还担心实际上确实需要多个实现的奇怪应用程序以及我们是否可以可靠地指定这些依赖项(例如,一个依赖于 L1 和 L2 的应用程序,因为那是那个应用程序的作者认为应用需要)。

我知道应用有办法block exclude 依赖项(请参阅 Joseph Earl 对语法的回答),因此应用可以依赖 L 但随后会阻止actionbarsherlock 上游依赖项(如果不需要)。虽然这可行,但对于我是 L 的作者的情况,我宁愿采用 L1/L2/L3 方法,因为这样看起来更简洁。

还有其他建议吗?

【问题讨论】:

  • 我投票支持分离的库,即使它在支持方面很痛苦。 L1 无需提及,因为 L3 对 L1 具有传递依赖
  • AAR 的缩写代表什么?
  • @gunar: Android 存档

标签: android maven gradle android-gradle-plugin


【解决方案1】:

我不知道有任何依赖管理功能可以帮助解决这个问题。

拥有一个包含许多依赖项的库以及删除不需要的依赖项的能力可能会起作用,但这样做会带来一些问题:

  • 您必须依靠 L 的用户来删除正确的依赖关系。
  • 您将在库中拥有使用 Proguard 不容易删除的类。 Proguard 不会删除任何扩展 Fragment/Activity/etc 的内容,并且 L 应该提供一个 proguard 规则文件以不删除扩展支持 Fragment/Activity/etc 的类...这将使删除不需要的类变得困难。
  • 某些实现可能有额外的资源,目前我们无法去除不需要的资源。

我认为拆分库是正确的做法,但是在我们对库项目进行调整之前,这样做会很复杂。一旦我们有了这个,我认为它会更容易处理。你不会有一个基本的 L1 和 L2/L3 扩展它。相反,您将拥有一个生成不同变体的库,每个变体都有自己的依赖项。

使用风味维度,您可以处理支持库与事件库组合,但如果添加更多维度,您肯定会得到一些爆炸。

多变体库的优势在于更容易在变体子集之间共享代码,而无需引入更多的子库。您还可以在主代码和风格之间进行双向引用,这在 L2/L3 中无法实现,具体取决于 L1。

至于支持 Ant/Eclipse,这肯定会很复杂。我会明确地忽略 Ant。如果有人只关心命令行构建,他们应该已经转移到 Gradle。 至于 Eclipse,我们会在某个时候提供 Gradle 支持,但如果您迫不及待,我理解。

【讨论】:

  • “我肯定会忽略 Ant。如果有人只关心命令行构建,他们应该已经迁移到 Gradle。” ——现在,我的猜测是大约 95% 的命令行非 CI 构建是通过 Ant 完成的。一旦 Android 版 Gradle 成为主流(例如 developer.android.com),从 Ant 到 Gradle 的迁移将会加速,但在 Ant 几乎完全消失之前要到 2015 年。此外,目前,如果您支持 Eclipse,您将免费获得 Ant 支持。无论如何,感谢您的回答,并在您发布时给出,请不要忘记睡觉! :-)
  • 没错,大多数使用 CI 的 ADT 用户可能会使用 Ant 来构建 CI。
【解决方案2】:

在您的应用程序项目中可以排除 Gradle 中的传递依赖项(例如,如果您知道不会使用 ActionBarSherlock,则排除它被包含),例如

 dependencies {
   compile('org.hibernate:hibernate:3.1') {
     // Excluding a particular transitive dependency:
     exclude module: 'cglib' //by artifact name
     exclude group: 'org.jmock' //by group
     exclude group: 'org.unwanted', module: 'iAmBuggy' //by both name and group
   }
 }

您还可以ProGuard(和DexGuard)压缩并删除未使用的代码。

Gradle doesn't yet support marking dependencies as optional 和 Maven 一样(可以在库项目中使用)。

【讨论】:

  • 刚刚注意到谁提出了这个问题!你可能知道大部分内容:)
  • 这就是我所说的“阻塞依赖”——我会很快更新这个问题,以指出你的答案。然而,exclude 感觉就像是应用程序开发人员可能需要的东西,但理想情况下是可以避免的。 ProGuard/DexGuard 肯定有帮助,但不是完美的解决方案。我的问题更多的是如何首先最小化对所有这些的需求,以更好的形式发布图书馆(或图书馆)。您链接到的未解决问题肯定会有所帮助。谢谢!
  • 拆分成多个库可能是你最好的选择(例如多模块项目)——我不知道有更好的解决方案。
【解决方案3】:

嗯,首先我要提到的是 Gradle 配置,它实际上是 Ivy 配置的一种反映:您部署到某个存储库的模块可以有不同的配置,并且每个配置可能有自己的一组依赖项。 http://www.gradle.org/docs/current/userguide/dependency_management.html#ssub:multi_artifact_dependencies

然而,Maven 不适合这种情况,因为 Maven 世界中没有配置。并且工件分类器将不允许您拥有不同的依赖项。

我们可以用 Maven 做的是从 POM 中删除编译时依赖声明,或者用provided 范围标记它们。 AFAIK 指定仅编译时依赖项的功能将在 Android Gradle 插件的未来版本中提供。 https://code.google.com/p/android/issues/detail?id=58626

【讨论】:

  • “但是有一个名为 apk 的单独配置用于构建 APK (AAR)。后一种配置基本上延长了编译时间,我们可能会调整添加到其中的内容。” -- 我只看到排除本地 JAR 的语法。 “因此,我们可以实现一个项目,其中添加了 android-support 包以编译配置,但它不会反映在上传到存储库的 POM 文件中,因此对您的库的依赖不会导致支持包的传递获取。” -- 抱歉,但我看不出apk.exclude 怎么可能。不过,感谢您的链接!
  • 对于语法,可以使用模块名称或组添加排除规则。我已经更新了答案。然而,我只是认为实际上 POM 不是从一种特定配置生成的。但是配置用于映射依赖范围。因此,也许可行的解决方案是使用“提供的”依赖项(根本不会映射到 POM)进行单独的配置,并使用此配置手动扩展编译类路径。
  • 刚刚收到有关相关问题的更新:code.google.com/p/android/issues/detail?id=58626
猜你喜欢
  • 1970-01-01
  • 2014-06-01
  • 1970-01-01
  • 2021-01-07
  • 1970-01-01
  • 1970-01-01
  • 2015-10-04
  • 2015-01-20
  • 1970-01-01
相关资源
最近更新 更多