tutorial studio productflavors now must flavors flavordimensions flavor configurar belong all actualizar android-gradle buildconfig

android-gradle - studio - flavordimensions



Gradle: cómo usar BuildConfig en una biblioteca android con una bandera que se configura en una aplicación (6)

El proyecto de Android My (gradle 1.10 y gradle plugin 0.8) consiste en una gran biblioteca de Android que es una dependencia de 3 aplicaciones de Android diferentes

En mi biblioteca, me encantaría poder utilizar una estructura como esta

if (BuildConfig.SOME_FLAG) { callToBigLibraries() }

como Proguard podría reducir el tamaño de la aplicación producida, en función del valor final de SOME_FLAG

Pero no puedo entender cómo hacerlo con gradle como:

* 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.

Intenté sin éxito jugar con BuildTypes y cosas como

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

¿Cuál es la forma correcta de compilar una BuildConfig compartida para mi biblioteca y mis aplicaciones cuyos indicadores se anularán al compilar las aplicaciones?


Como solución alternativa, puede usar este método, que usa la reflexión para obtener el valor de campo de la aplicación (no de la biblioteca):

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

Para obtener el campo DEBUG , por ejemplo, solo llame a esto desde su Activity :

boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");

También he compartido esta solución en AOSP Issue Tracker .


La siguiente solución / solución alternativa funciona para mí. Fue publicado por un tipo en el rastreador de problemas de Google:

Intente configurar publishNonDefault en true en el proyecto de la biblioteca :

android { ... publishNonDefault true ... }

Y agregue las siguientes dependencias al proyecto de la aplicación que está utilizando la biblioteca:

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

De esta forma, el proyecto que usa la biblioteca incluye el tipo de compilación correcto de la biblioteca.


No puede hacer lo que quiera porque BuildConfig.SOME_FLAG no se propagará correctamente a su biblioteca; los tipos de compilación en sí mismos no se propagan a las bibliotecas; siempre se crean como LIBERACIÓN. Este es el error https://code.google.com/p/android/issues/detail?id=52962

Para callToBigLibraries() : si tiene control sobre todos los módulos de la biblioteca, puede asegurarse de que todo el código tocado por callToBigLibraries() esté en clases y paquetes que pueda dividir limpiamente con ProGuard, luego use reflection para que pueda acceda a ellos si existen y degradarse con gracia si no lo hacen. Básicamente estás haciendo lo mismo, pero estás haciendo el control en tiempo de ejecución en lugar de en tiempo de compilación, y es un poco más difícil.

Avíseme si tiene problemas para descubrir cómo hacer esto; Podría proporcionar una muestra si la necesita.


Para el caso en que applicationId no es el mismo que el paquete (es decir, múltiples applicationIds por proyecto) Y desea acceder desde un proyecto de biblioteca:

Use Gradle para almacenar el paquete base en los recursos.

En 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}" }

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


Utilizo una clase BuildConfigHelper estática tanto en la aplicación como en la biblioteca, de modo que puedo tener los paquetes BuildConfig establecidos como variables estáticas finales en mi biblioteca.

En la aplicación, coloca una clase como esta:

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

Y en la biblioteca:

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

Luego, en cualquier lugar de su biblioteca donde desee verificar BuildConfig.DEBUG, puede verificar BuildConfigHelper.DEBUG y acceder desde cualquier lugar sin contexto, y lo mismo para las otras propiedades. Lo hice de esta manera para que la biblioteca funcione con todas mis aplicaciones, sin necesidad de pasar un contexto o establecer el nombre del paquete de otra manera, y la clase de aplicación solo necesita que se modifique la línea de importación para agregarla a un nuevo solicitud

Editar: me gustaría reiterar que esta es la forma más fácil (y solo una de las enumeradas aquí) de asignar los valores a las variables estáticas finales en la biblioteca desde todas sus aplicaciones sin necesidad de un contexto o una codificación rígida. nombre del paquete en algún lugar, que es casi tan bueno como tener los valores en la biblioteca predeterminada BuildConfig de todos modos, para el mantenimiento mínimo de cambiar esa línea de importación en cada aplicación.


usa ambos

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

Quiero llamar como a continuación

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