【问题标题】:How To Exclude Specific Resources from an AAR Depedency?如何从 AAR 依赖项中排除特定资源?
【发布时间】:2015-12-21 14:18:12
【问题描述】:

模块的build.gradle 文件是否有一种相当简单的方法来指示应排除依赖项中的某些文件?我对从 AAR 中排除某些资源特别感兴趣。


LeakCanary 是一个有趣的库,用于帮助追踪内存泄漏。但是,它具有 21 或更高的 compileSdkVersion 的未记录要求。虽然大多数项目不应该有这个问题,但图书馆在没有充分理由的情况下要求某个compileSdkVersion 是不合适的。作为一般政策的一部分,开发团队可能冻结了他们的compileSdkVersion,只在他们的应用程序或其他内容的主要版本更新中更改这些类型的设置。

在这种情况下,至少对于 v1.3.1 的 LeakCanary,需要 compileSdkVersion 的唯一原因是 AFAICT,因为 AAR 有一个 res/values-v21/ 目录,其中包含一个从 Theme.Material 继承的主题定义。此主题由诊断活动使用。最终用户永远不会看到该活动,只有 debug 构建中的开发人员才能看到。坦率地说,那个活动看起来像什么,就主题而言,并不重要。强制compileSdkVersion 21 只是为了让诊断活动有一个特定的主题,恕我直言,愚蠢。

如果作为compile 指令的一部分,我们可以说“嘿,请跳过此AAR 中的res/values-v21/,好吗?”。由于-v21 主题只是提供了在别处定义的主题的替代定义,因此删除-v21 主题不会在运行时破坏构建或破坏事物,而只会给我们一个Holo-主题诊断活动。

我看不到 this answer 如何处理依赖项。我也不确定if it is complete, and it certainly does not appear to be supported。它也并不真正符合“简单”的条件——我不希望有人尝试将其放入 build.gradle 文件中,只是为了阻止来自 LeakCanary 等诊断库的单个文件。

那么,有没有比这更简单的东西可以与当前版本的 Android Plugin for Gradle 一起使用?

【问题讨论】:

  • 我认为,LeakCanary (github.com/square/leakcanary) 的解决方法是分叉它并使用适当的compileSdkVersion 编译您自己的版本。我不确定它是否可以算作问题的答案。
  • @KonstantinLoginov:LeakCanary 似乎有相当多的相互连接的移动部件,这就是为什么我怀疑分叉会这么简单。我做了足够多的探索以确定唯一的 Android 5.0+ 功能是Theme.Material,这就是导致我提出这个问题的原因。虽然我在 LeakCanary 的背景下提出了这个问题,但这个问题超越了那个图书馆。
  • 如果 google 当前的 gradle 插件源是公开的就好了。 DSL 源代码 [1] 的标签远远落后于发布标签 [2]。 [1]android.googlesource.com/platform/tools/gradle/+refs [2]jcenter.bintray.com/com/android/tools/build/gradle

标签: android android-gradle-plugin


【解决方案1】:

编辑:

为您编写了高级 gradle 任务:

final List<String> exclusions = [];

Dependency.metaClass.exclude = { String[] currentExclusions ->
    currentExclusions.each {
        exclusions.add("${getGroup()}/${getName()}/${getVersion()}/${it}")
    }
    return thisObject
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile ('com.android.support:appcompat-v7:20.+')
    debugCompile ('com.squareup.leakcanary:leakcanary-android:1.3.1')
            .exclude("res/values-v21/values-v21.xml")
    releaseCompile ('com.squareup.leakcanary:leakcanary-android-no-op:1.3.1')
}

tasks.create("excludeTask") << {
    exclusions.each {
        File file = file("${buildDir}/intermediates/exploded-aar/${it}")
        println("Excluding file " + file)
        if (file.exists()) {
            file.delete();
        }
    }
}

tasks.whenTaskAdded({
    if (it.name.matches(/^process.*Resources$/)) {
        it.dependsOn excludeTask
    }
})

现在您可以在每个依赖项上使用方法.exclude(),提供路径列表,您想从指定的依赖项中排除。 此外,您可以堆叠.exclude() 方法调用。

【讨论】:

  • 我认为您的LEAKCANARY_ARTIFACT_NOOP 定义有错误。虽然这很有趣,但它并没有回答实际问题:“模块的build.gradle 文件是否有一种相当简单的方法来指示应该排除依赖项中的某些文件?”它以另一种方式(修改资源)解决了我的示例场景。不过谢谢!
  • 我认为没有任何方法可以开箱即用地做你想做的事。但是您可以编写自己的任务,就像这样,将在资源合并之前执行,并删除您不需要的资源。每个 aar 模块的所有资源都存储在 build/exploded-aar 目录中,您可以从那里安全地删除它们,它们根本不会被处理。但是,关于简单性:您可以为“编译”闭包编写自己的包装器,它可以获取将在任务中排除的文件/目录数组。我明天会试着做这个。
  • 编辑了答案。我认为现在排除要简单得多。
  • 这不是一个完整的解决方案,从这个角度来看,如果你把它放在一个库模块中,它不会阻止整个 AAR 文件包含在依赖它的模块中。我不确定解决方案是什么。
  • 太糟糕了,它在 2020 年不再适用于 gradle 4.0.1
【解决方案2】:

尝试compileOnly关键字来标记资源仅用于编译。

dependencies {
      compileOnly fileTree(include: ['*.jar'], dir: 'libs')
}

【讨论】:

    【解决方案3】:

    我相信您可以使用 Android Gradle Plugin DSL 的 PackagingOptions 工具更优雅地解决这个问题。

    我自己可以使用它来排除一些我不需要由项目中的 AAR 引入的本机库。

    android {
        ...
        packagingOptions {
            exclude '/lib/armeabi-v7a/<file_to_exclude>'
        }
    }
    

    对于问题中概述的情况,我相信这会起作用:

    android {
        ...
        packagingOptions {
            exclude '/res/values-v21/<file_to_exclude>'
        }
    }
    

    【讨论】:

    • 我会在某个时候试一试,但我怀疑这是否可行。首先,pacakgingOptions 在构建过程中为时已晚 - 由于compileSdkVersion 在它到达之前构建应该失败。其次,AFAIK,packagingOptions是指根据它们在APK中的位置排除事物,并且XML资源不会像普通文件一样进入APK。不过谢谢!
    • 有机会动态地做吗? stackoverflow.com/questions/46543271/…
    • packagingOptions 是我需要从 aar 包的 classes.jar 中排除特定文件
    猜你喜欢
    • 2016-06-20
    • 1970-01-01
    • 2011-03-03
    • 1970-01-01
    • 2013-04-27
    • 2020-04-19
    • 2018-12-16
    • 2015-02-19
    • 2021-12-25
    相关资源
    最近更新 更多