在Android中的库项目中使用Gradle构建变体

22

我正在尝试使用Gradle配置一个包含一些外部库的项目。使用Gradle,我可以为主应用程序设置不同的环境配置(在配置文件中的类中),使用Build Variants使我可以根据这些变量执行代码。

问题是我如何为库项目做同样的操作?我为此项目创建了这个库,并希望为不同的场景设置不同的Build Variants。

例如:在库中,在调试模式下运行时,请打印所有日志,以便在开发过程中查看它们。在发布模式下则不需要。

文件结构:

src ----- > debug -> java -> config -> PlayerEnvConfig
            main -> com.mypackagename -> etc...
            release -> java -> config -> PlayerEnvConfig

调试中的代码: package config;

/**
 * Environment configuration for Release
*/
public final class PlayerEnvConfig {
    public static final boolean USE_REPORTING = true;
    public static final boolean USE_ANALYTICS = true;
    public static final boolean USE_LOGGING = false;
    public static final boolean USE_DEBUG_LOGGING = false;
    public static final boolean USE_DEBUGING = false;
}

发布版本中的代码:

package config;

/**
 * Environment configuration for Release
*/
public final class PlayerEnvConfig {
    public static final boolean USE_REPORTING = true;
    public static final boolean USE_ANALYTICS = true;
    public static final boolean USE_LOGGING = false;
    public static final boolean USE_DEBUG_LOGGING = false;
    public static final boolean USE_DEBUGING = false;
}

问题是对于主项目,我可以使用这些 Build types 为不同的场景配置应用程序,但我该如何为库项目做到相同的配置呢?

因为目前从我在 http://tools.android.com/tech-docs/new-build-system/user-guide 中读到的来看,库只会在测试时使用调试模式。

有什么想法吗?

谢谢!

6个回答

16

这是一篇来自谷歌代码问题的@bifmadei答案,对我很有帮助:

过时的方法 1: 尝试在依赖项目中设置这个。

android {
    publishNonDefault true
    ...
}

已过时2: 从gradle 4.10.1 开始,默认情况下publishNonDefault为true。因此,请按照以下建议使用:

将其包含在使用它的项目中即可。

dependencies {
    releaseCompile project(path: ':theotherproject', configuration: 'release')
    debugCompile project(path: ':theotherproject', configuration: 'debug')
}

来自这里: https://code.google.com/p/android/issues/detail?id=66805

请注意,使用 implementation 指令分离后,releaseCompiledebugCompile 将变得过时:https://dev59.com/-1sX5IYBdhLWcg3whvsn#44364851

更新:

以前的方法仍然可以与自定义构建配置一起使用:

implementation project(path: ':theotherproject', configuration: 'staging')

1
这对我来说确实非常有效。请注意,您也可以使用自定义的buildTypes。我有一个用于模拟器的buildType,因此我将simulatorCompile添加到我的gradle文件中。如果您已经正确设置了它,当您通过AndroidStudio 1.2.2切换Build Variant时,正确的Build Variant将会传递到依赖模块中。 - biddster
很遗憾,在gradle-experimental中“releaseCompile”不起作用。 - IgorGanapolsky

12

我不确定你的配置出了什么问题,但就你所需求而言,我会采用不同的方法。

在gradle构建文件中,您可以使用buildConfig关键字将特定的行添加到生成的BuildConfig.java类中。

因此,您可以在build.gradle文件中添加以下内容:

    release {
        buildConfig "public static final String USE_REPORTING = true;"
    }
    debug {

        buildConfig "public static final String USE_REPORTING = false;"
    }

所以只有一个PlayerEnvConfig

public static final boolean USE_REPORTING = BuildConfig.USE_REPORTING;

甚至可以不再使用PlayerEnvConfig,而直接使用BuildConfig类。


编辑自从更新后,语法已经改变:

buildConfigField "<type>", "<name>", "<value>"

7
谢谢tbruyelle,我已经尝试了这种方式(顺便说一下,这种方式更加智能),但问题仍然存在。问题在于构建应用程序时库始终以发布模式构建(即使我在Android Studio中选择了调试模式)。在我之前发的链接中解释了这一点。但是是否存在任何解决方法?“调试类型用于测试应用程序。发布类型用于使用该库的项目。” - Javier Tarazaga

4
这个问题在https://code.google.com/p/android/issues/detail?id=52962中有记录。正如你所发现的,构建类型没有传播到库项目中,并且没有一个好的解决方法。如果您可以控制库项目的代码,则可以使调试状态成为可变全局变量,并在启动时从主应用程序中设置它。这是一种不太规范的方法,它的缺点是编译器无法优化未使用的代码路径以达到发布版的效果,但除非出现了什么异常情况,否则这应该是有效的。

3

更新 - 自本帖发布以来,Gradle构建流程已经取得了很大进展,因此本回答可能不是推荐的最佳实践,新的更改甚至可能会破坏它。请自行决定。

我认为整个项目结构和配置有些混淆。假设您有以下build.gradle配置:

sourceSets {

    main {
        manifest.srcFile 'src/main/AndroidManifest.xml'
        java.srcDirs = ['src/main/java']
        //resources.srcDirs = ['src/main']
        //aidl.srcDirs = ['src/main']
        //renderscript.srcDirs = ['src/main']
        res.srcDirs = ['src/main/res']
        assets.srcDirs = ['src/main/assets']
    }

    debug.setRoot('build-types/debug')
    release.setRoot('build-types/release')
}

您的项目文件夹结构应按以下方式组织。
project_root
   -src
      -main
         -java
            -com
               -example
   -build-types
      -debug
         -java
            -com
               -example
                  -FooBar.java
      -release
         -java
            -com
               -example
                  -FooBar.java
FooBar.java不应该放在prooject_root/src/main/java/com/example中,而应该放在debugrelease文件夹中,这些文件夹位于src文件夹之外但是在build-types文件夹内。这可以通过setRoot('build-types/*****')方法进行配置。很多人会因为看到“debug/java”和“main/java”而感到困惑,后者以“src/main/java”的方式被引用,结果把“debug/java”放在了错误的文件夹中。希望这能有所帮助。

对于涉及其他库的更复杂环境,您可以查看我的答案:https://dev59.com/KGIk5IYBdhLWcg3wdd0l#19918834


0

正如Scott所指出的那样,这是Gradle已知的缺陷。作为一种解决方法,您可以使用此方法,该方法使用反射从应用程序(而不是库)获取字段值:

/**
 * Gets a field from the project's BuildConfig. This is useful when, for example, flavors
 * are used at the project level to set custom fields.
 * @param context       Used to find the correct file
 * @param fieldName     The name of the field-to-access
 * @return              The value of the field, or {@code null} if the field is not found.
 */
public static Object getBuildConfigValue(Context context, String fieldName) {
    try {
        Class<?> clazz = Class.forName(context.getPackageName() + ".BuildConfig");
        Field field = clazz.getField(fieldName);
        return field.get(null);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

要获取DEBUG字段,例如,只需从您的Activity调用此函数:
boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");

我也在AOSP问题跟踪器上分享了这个解决方案。


0

如你所提到的,这在库项目中是不可能的。

你可以将库项目改成应用程序项目。这似乎可以正常工作(至少在理论上,我自己没有测试过)。

另一种方法就是在应用程序项目中覆盖那个类。当合并发生时,会选择你的应用程序项目类,并且你将拥有这些值。


据我所知,应用程序模块不能依赖于其他应用程序模块。我曾经有过相关的参考资料,但现在找不到了。 - Hannes Struß
是的,我也发现了。我相信在旧的构建系统中是可能的,但没有传播到Gradle。 - Saad Farooq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接