Gradle:如何在android库中使用BuildConfig,并在应用程序中设置标志

我的(Gradle 1.10和Gradle插件0.8)基于Android的项目包括一个大的Android库,是一个依赖于3个不同的android应用程序

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

if (BuildConfig.SOME_FLAG) { callToBigLibraries() } 

因为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. 

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

 release { // packageNameSuffix "library" buildConfigField "boolean", "SOME_FLAG", "true" } debug { //packageNameSuffix "library" buildConfigField "boolean", "SOME_FLAG", "true" } 

什么是正确的方法来build立一个共享的BuildConfig为我的图书馆和我的应用程序的标志将被覆盖在应用程序的内置?

非常感谢任何见解

你不能做你想做的事情,因为BuildConfig.SOME_FLAG不会正确地传播到你的库中。 构buildtypes本身不会传播到库 – 它们始终构build为RELEASE。 这是错误https://code.google.com/p/android/issues/detail?id=52962

为了解决这个问题:如果你可以控制所有的库模块,你可以确保callToBigLibraries()所涉及的所有代码都在类和包中,你可以用ProGuard干净地分解,然后使用reflection,这样你就可以如果它们存在,则访问它们,如果它们不存在则优雅地降级。 你基本上做同样的事情,但是你在运行时进行检查,而不是编译时间,这有点困难。

让我知道如果你不知道如何做到这一点, 如果你需要的话,我可以提供一个样本。

作为解决方法,您可以使用此方法,该方法使用reflection从应用程序(而不是库)中获取字段值:

 /** * 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问题跟踪器上分享了这个解决scheme。

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

尝试在项目publishNonDefault设置为true

 android { ... publishNonDefault true ... } 

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

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

这样,使用库的项目就包含了库的正确的构buildtypes。

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

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

 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; } 

而在图书馆里:

 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; } } } 

然后,在你想要检查BuildConfig.DEBUG的库中的任何地方,你都可以检查BuildConfigHelper.DEBUG,并在没有上下文的情况下从任何地方访问它,其他属性也一样。 我这样做是为了让库可以和我所有的应用程序一起工作,而不需要传递一个上下文或者以其他方式设置包名,应用程序类只需要改变导入行就可以了,应用

编辑:我只想重申,这是最简单的(也是唯一一个列在这里)的方式来获取值分配给您的所有应用程序库中的最终静态variables,而无需上下文或硬编码包的名称,这几乎与默认库BuildConfig中的值一样好,以便在每个应用程序中最小化维护导入行。

对于applicationId与包不相同的情况(即每个项目有多个applicationIds)并且您想从一个库项目中访问:

使用Gradle将基础包存储在资源中。

在主/ 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}" } 

在Java中:

 public static boolean getDebug(Context context) { Object obj = getBuildConfigValue("DEBUG", context); if (obj instanceof Boolean) { return (Boolean) o; } else { return false; } } private static Object getBuildConfigValue(String fieldName, Context context) { int resId = context.getResources().getIdentifier("build_config_package", "string", context.getPackageName()); // try/catch blah blah Class<?> clazz = Class.forName(context.getString(resId) + ".BuildConfig"); Field field = clazz.getField(fieldName); return field.get(null); } 

同时使用

 my build.gradle // ... productFlavors { internal { // applicationId "com.elevensein.sein.internal" applicationIdSuffix ".internal" resValue "string", "build_config_package", "com.elevensein.sein" } production { applicationId "com.elevensein.sein" } } 

我想打电话如下

 Boolean isDebug = (Boolean) BuildConfigUtils.getBuildConfigValue(context, "DEBUG"); 

BuildConfigUtils.java

 public class BuildConfigUtils { public static Object getBuildConfigValue (Context context, String fieldName) { Class<?> buildConfigClass = resolveBuildConfigClass(context); return getStaticFieldValue(buildConfigClass, fieldName); } public static Class<?> resolveBuildConfigClass (Context context) { int resId = context.getResources().getIdentifier("build_config_package", "string", context.getPackageName()); if (resId != 0) { // defined in build.gradle return loadClass(context.getString(resId) + ".BuildConfig"); } // not defined in build.gradle // try packageName + ".BuildConfig" return loadClass(context.getPackageName() + ".BuildConfig"); } private static Class<?> loadClass (String className) { Log.i("BuildConfigUtils", "try class load : " + className); try { return Class.forName(className); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } private static Object getStaticFieldValue (Class<?> clazz, String fieldName) { try { return clazz.getField(fieldName).get(null); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } }