Gradle:如何在android-library中使用BuildConfig,并在应用程序中设置一个标志

Lak*_*mon 54 buildconfig android-gradle-plugin

我的(gradle 1.10和gradle插件0.8)基于android的项目包含一个大的android-library,它是3个不同的android-app的依赖

在我的图书馆,我希望能够使用这样的结构

if (BuildConfig.SOME_FLAG) {
    callToBigLibraries()
}
Run Code Online (Sandbox Code Playgroud)

因为proguard可以根据SOME_FLAG的最终值减小生成的apk的大小

但我无法想象如何用gradle做到:

* the BuildConfig produced by the library doesn't have the same package name than the app
* I have to import the BuildConfig with the library package in the library
* The apk of an apps includes the BuildConfig with the package of the app but not the one with the package of the library.
Run Code Online (Sandbox Code Playgroud)

我尝试使用BuildTypes和类似的东西没有成功

release {
    // packageNameSuffix "library"
    buildConfigField "boolean", "SOME_FLAG", "true"
}
debug {
    //packageNameSuffix "library"
    buildConfigField "boolean", "SOME_FLAG", "true"
}
Run Code Online (Sandbox Code Playgroud)

为我的库和我的应用程序构建共享BuildConfig的正确方法是什么,其标志将在应用程序中构建时被覆盖?

Phi*_*hil 42

作为一种变通方法,您可以使用此方法,该方法使用反射从应用程序(而不是库)获取字段值:

/**
 * 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;
}
Run Code Online (Sandbox Code Playgroud)

DEBUG例如,要获得该字段,只需从您的Activity:

boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");
Run Code Online (Sandbox Code Playgroud)

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

  • @Vlad我想象有一种方法可以改变输出`BuildConfig`的位置 - 但是根据我的经验,这可以保证默认设置. (3认同)
  • 在您的applicationId与Java类的包名称不同之前,这似乎工作正常.(例如,如果您要构建此处提到的变体:http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename).似乎context.getPackageName()将返回applicationId,它现在可能不是BuildConfig.class所在的Java类的包.有什么解决方法吗? (3认同)
  • 是否可以将“ BuildConfig”包含在该软件包中? (2认同)
  • 我最近用这个方法发现了一个 ClassNotFoundException:如果你在 gradle 的 buildConfig 中声明了一个后缀(例如,如果你想要相同的应用程序但具有不同的包名称,具体取决于调试或发布版本,用于测试),`context. getPackageName()`将返回带后缀的包名称,但 BuildConfig 仍将部署在不带后缀的 packageName 上。因此,当类是“com.example.app.BuildConfig”时,该方法将查找“com.example.app.suffix.BuildConfig”类。有什么想法吗? (2认同)
  • 当 ProGuard 启动时中断! (2认同)

All*_*ode 32

以下解决方案/解决方法适用于我.它是由谷歌问题跟踪器中的一些人发布的:

尝试设置publishNonDefaulttrue项目:

android {
    ...
    publishNonDefault true
    ...
}
Run Code Online (Sandbox Code Playgroud)

并将以下依赖项添加到使用该库的应用程序项目:

dependencies {
    releaseCompile project(path: ':library', configuration: 'release')
    debugCompile project(path: ':library', configuration: 'debug')
}
Run Code Online (Sandbox Code Playgroud)

这样,使用该库的项目包括库的正确构建类型.

  • 感谢Varun,它就像一个魅力 (2认同)

Sco*_*rta 19

你无法做你想做的事,因为BuildConfig.SOME_FLAG它不会被正确地传播到你的图书馆; 构建类型本身不会传播到库 - 它们总是构建为RELEASE.这是错误https://code.google.com/p/android/issues/detail?id=52962

要解决这个问题:如果您可以控制所有库模块,那么您可以确保所有涉及的代码callToBigLibraries()都在可以使用ProGuard完全切割的类和包中,然后使用反射以便您可以访问它们.如果不存在,它们就会存在并优雅地降级.你基本上做了同样的事情,但是你在运行时进行检查而不是编译时间,这有点困难.

如果你在弄清楚如何做到这一点,请告诉我.如果需要,我可以提供样品.


Kan*_*ley 7

我在应用程序和库中都使用了静态 BuildConfigHelper 类,这样我就可以将 BuildConfig 包设置为库中的最终静态变量。

在应用程序中,放置一个这样的类:

package com.yourbase;

import com.your.application.BuildConfig;

public final class BuildConfigHelper {

    public static final boolean DEBUG = BuildConfig.DEBUG;
    public static final String APPLICATION_ID = BuildConfig.APPLICATION_ID;
    public static final String BUILD_TYPE = BuildConfig.BUILD_TYPE;
    public static final String FLAVOR = BuildConfig.FLAVOR;
    public static final int VERSION_CODE = BuildConfig.VERSION_CODE;
    public static final String VERSION_NAME = BuildConfig.VERSION_NAME;

}
Run Code Online (Sandbox Code Playgroud)

在图书馆:

package com.your.library;

import android.support.annotation.Nullable;

import java.lang.reflect.Field;

public class BuildConfigHelper {

    private static final String BUILD_CONFIG = "com.yourbase.BuildConfigHelper";

    public static final boolean DEBUG = getDebug();
    public static final String APPLICATION_ID = (String) getBuildConfigValue("APPLICATION_ID");
    public static final String BUILD_TYPE = (String) getBuildConfigValue("BUILD_TYPE");
    public static final String FLAVOR = (String) getBuildConfigValue("FLAVOR");
    public static final int VERSION_CODE = getVersionCode();
    public static final String VERSION_NAME = (String) getBuildConfigValue("VERSION_NAME");

    private static boolean getDebug() {
        Object o = getBuildConfigValue("DEBUG");
        if (o != null && o instanceof Boolean) {
            return (Boolean) o;
        } else {
            return false;
        }
    }

    private static int getVersionCode() {
        Object o = getBuildConfigValue("VERSION_CODE");
        if (o != null && o instanceof Integer) {
            return (Integer) o;
        } else {
            return Integer.MIN_VALUE;
        }
    }

    @Nullable
    private static Object getBuildConfigValue(String fieldName) {
        try {
            Class c = Class.forName(BUILD_CONFIG);
            Field f = c.getDeclaredField(fieldName);
            f.setAccessible(true);
            return f.get(null);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

然后,在您想要检查 BuildConfig.DEBUG 的库中的任何地方,您都可以检查 BuildConfigHelper.DEBUG 并从任何没有上下文的地方访问它,其他属性也是如此。我这样做是为了使库可以与我的所有应用程序一起使用,而无需以其他方式传入上下文或设置包名称,并且应用程序类只需要更改导入行以适应将其添加到新的应用

编辑:我想重申一下,这是从所有应用程序获取要分配给库中最终静态变量的值的最简单(此处仅列出一种)方法,无需上下文或硬编码某处的包名,这几乎和在默认库 BuildConfig 中的值一样好,对于在每个应用程序中更改导入行的最小维护。

  • 库中的修复类名引用(常量 BUILD_CONFIG )并不是解决此问题的可靠解决方案。 (2认同)

Mar*_*ark 5

For the case where the applicationId is not the same as the package (i.e. multiple applicationIds per project) AND you want to access from a library project:

Use Gradle to store the base package in resources.

In main/AndroidManifest.xml:

android {
    applicationId "com.company.myappbase"
    // note: using ${applicationId} here will be exactly as above
    // and so NOT necessarily the applicationId of the generated APK
    resValue "string", "build_config_package", "${applicationId}"
}
Run Code Online (Sandbox Code Playgroud)

In Java:

android {
    applicationId "com.company.myappbase"
    // note: using ${applicationId} here will be exactly as above
    // and so NOT necessarily the applicationId of the generated APK
    resValue "string", "build_config_package", "${applicationId}"
}
Run Code Online (Sandbox Code Playgroud)