BuildConfig.DEBUG siempre es falso al crear proyectos de biblioteca con gradle

83

BuildConfig.DEBUG no funciona (= lógicamente establecido en falso) cuando ejecuto mi aplicación en modo de depuración. Uso Gradle para construir. Tengo un proyecto de biblioteca donde hago esta verificación. BuildConfig.java se ve así en la carpeta de depuración de compilación:

/** Automatically generated the file. DO NOT MODIFY */
package common.myProject;

public final class BuildConfig {
    public static final boolean DEBUG = Boolean.parseBoolean("true");

}

y en la carpeta de lanzamiento:

public static final boolean DEBUG = false;

tanto en el proyecto de biblioteca como en el proyecto de aplicación.

Intenté solucionar esto comprobando una variable que se establece en una clase de mi proyecto. Esta clase hereda de la biblioteca y comienza al inicio.

<application
        android:name=".MyPrj" ...

Esto conduce a otro problema: es que utilizo mi variable DEBUG en un DataBaseProvider que se ejecuta antes que la clase de aplicación y no se ejecutará correctamente debido a este error.

usuario1324936
fuente
Es un comportamiento normal. ¿Dónde está el problema? Tienes que cambiar entre BuildVariants
Gabriele Mariotti
1
El archivo BuildConfig se genera correctamente pero en tiempo de ejecución es falso. Tengo el mismo problema.
jophde

Respuestas:

52

Este es el comportamiento esperado para esto.

Los proyectos de biblioteca solo publican sus variantes de lanzamiento para el consumo de otros proyectos o módulos.

Estamos trabajando para solucionar este problema, pero esto no es trivial y requiere una cantidad significativa de trabajo.

Puede rastrear el problema en https://code.google.com/p/android/issues/detail?id=52962

Xavier Ducrohet
fuente
4
Solución alternativa: instalado en BuildConfig.DEBUG, cree otra variable booleana en lib-project, por ejemplo, BuildConfig.RELEASE y vincúlela con buildType de la aplicación. Detalles: gist.github.com/almozavr/d59e770d2a6386061fcb
Oleksii Malovanyi
La solución proporcionada por DodoEnte en el rastreador de problemas funciona bien, sin necesidad de una solución alternativa.
3c71
Ese ya no es el caso. Hay una solución adecuada para eso. Consulte mi respuesta para obtener más información.
Niklas
Eso es cierto, pero tiene que hacerse manualmente y no se adapta muy bien a los sabores. Queremos que esto sea más automático en el futuro.
Xavier Ducrohet
@XavierDucrohet Este es un comportamiento inesperado y contrario a la intuición. Definitivamente deberías intentar arreglarlo si puedes.
Radu
86

Con Android Studio 1.1 y teniendo también la versión de gradle en 1.1 es posible:

Biblioteca

android {
    publishNonDefault true
}

Aplicación

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

La documentación completa se puede encontrar aquí http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

EDITAR :

El problema se acaba de marcar como solucionado para Android Studio Gradle versión 3.0. Allí solo puede usar implementation project(path: ':library')y seleccionará la configuración correcta automáticamente.

Niklas
fuente
5
De esta manera funciona. Pero hay un inconveniente: se llama a ": library: assembleRelease" incluso si está haciendo ": app: assembleDebug", y esto resultará en un tiempo de construcción más largo.
Alan Zhiliang Feng
Vaya, finalmente actualizaron un poco esa página y finalmente agregaron esta función.
Jared Burrows
¡Gracias, esto hizo el trabajo!
Aykut Çevik
@Konica El tiempo de construcción más largo de Gradle es un pequeño precio a pagar, ¡es complicado y largo de todos modos! ¡Esto funcionó maravillosamente! ¡Bien hecho!
Radu
¿Necesitamos agregar la parte "Aplicación" para cada biblioteca que usamos? Si es así, eso es bastante molesto ...
desarrollador de Android
46

Compruebe que imports, a veces, BuildConfig se importa de cualquier clase de biblioteca sin querer. Por ejemplo:

import io.fabric.sdk.android.BuildConfig;

En este caso, BuildConfig.DEBUG siempre devolverá falso ;

import com.yourpackagename.BuildConfig;

En este caso, BuildConfig.DEBUG devolverá su variante de compilación real .

Gent Berani
fuente
8

Esta es como la respuesta de Phil, excepto que no necesita el contexto:

private static Boolean sDebug;

/**
 * Is {@link BuildConfig#DEBUG} still broken for library projects? If so, use this.</p>
 * 
 * See: https://code.google.com/p/android/issues/detail?id=52962</p>
 * 
 * @return {@code true} if this is a debug build, {@code false} if it is a production build.
 */
public static boolean isDebugBuild() {
    if (sDebug == null) {
        try {
            final Class<?> activityThread = Class.forName("android.app.ActivityThread");
            final Method currentPackage = activityThread.getMethod("currentPackageName");
            final String packageName = (String) currentPackage.invoke(null, (Object[]) null);
            final Class<?> buildConfig = Class.forName(packageName + ".BuildConfig");
            final Field DEBUG = buildConfig.getField("DEBUG");
            DEBUG.setAccessible(true);
            sDebug = DEBUG.getBoolean(null);
        } catch (final Throwable t) {
            final String message = t.getMessage();
            if (message != null && message.contains("BuildConfig")) {
                // Proguard obfuscated build. Most likely a production build.
                sDebug = false;
            } else {
                sDebug = BuildConfig.DEBUG;
            }
        }
    }
    return sDebug;
}
Jared Rummler
fuente
De acuerdo con esta publicación de blog ( blog.javia.org/static-the-android-application-package ), nunca debe llamar al método currentPackageName desde ningún hilo que no sea el hilo de actividad (hilo de IU). Aunque una solución genial.
Rolf ツ
@Rolf ツ Bueno, podrías usar el contexto de la aplicación en su lugar.
desarrollador de Android
6

Como solución alternativa, puede utilizar este método, que utiliza la reflexión para obtener el valor del 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 DEBUGcampo, por ejemplo, simplemente llame a esto desde su Activity:

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

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

Phil
fuente
@shkschneider ¿qué línea? ¿Puedes publicar tu excepción?
Phil
3
Puede ser útil para otros: tenga cuidado con el uso de applicationIdSuffixen Gradle que haría que la .BuildConfigclase no sea accesible desde este código anterior.
shkschneider
5

No es realmente la forma correcta de verificar si está en versión de depuración, pero puede verificar si la aplicación en sí es depurable a través de:

private static Boolean sIsDebuggable;

public static boolean isDebuggable(Context context) {
    if (sIsDebuggable == null)
        sIsDebuggable = (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
    return sIsDebuggable;
}

El comportamiento predeterminado de las aplicaciones y bibliotecas coincidirá perfectamente con él.

Si necesita una solución alternativa mejor, puede usar esto en su lugar:

public static boolean isInDebugFlavour(Context context) {
    if (sDebugFlavour == null) {
        try {
            final String packageName = context.getPackageName();
            final Class<?> buildConfig = Class.forName(packageName + ".BuildConfig");
            final Field DEBUG = buildConfig.getField("DEBUG");
            DEBUG.setAccessible(true);
            sDebugFlavour = DEBUG.getBoolean(null);
        } catch (final Throwable t) {
            sDebugFlavour = false;
        }
    }
    return sDebugFlavour;
}
desarrollador de Android
fuente
2

Puede crear su propia clase BuildConfig para cada tipo de compilación usando gradle

public class MyBuildConfig
{
    public static final boolean DEBUG = true;
}

para /src/debug/.../MyBuildConfig.java y ...

public class MyBuildConfig
{
    public static final boolean DEBUG = false;
}

para /src/release/.../MyBuildConfig.java

Entonces usa:

if (MyBuildConfig.DEBUG)
    Log.d(TAG, "Hey! This is debug version!");
Racimo
fuente
¿"..." para el nombre del paquete de la biblioteca? Si es así, esto no parece funcionar. No puedo acceder a la clase.
desarrollador de Android
2

Aquí hay otra solución.

1) Crea una interfaz

public interface BuildVariantDetector {

    boolean isDebugVariant();

}

2) Utilice esta interfaz en la clase de aplicación (módulo de aplicación)

public class MyApplication extends Application implements BuildVariantDetector {

    @Override
    public boolean isDebugVariant() {
        return BuildConfig.DEBUG; //application (main module) Buildonfig
    }

}

3) Y luego en el módulo de biblioteca:

boolean debugVariant = ((BuildVariantDetector)getApplication()).isDebugVariant();
Dominik Suszczewicz
fuente
Esto no funciona. BuildConfig.DEBUG sigue siendo falso para mí.
DiscDev
Solución simple y elegante. Solo asegúrese de importar BuildConfig del módulo de la aplicación, no el de la biblioteca. Ese es un error muy furtivo.
WindRider
1

Tuvimos el mismo problema. Se me ocurrió algo como esto:

Tenemos un SDK (biblioteca) y un proyecto de demostración, la jerarquía se ve así:

Parent
  |
  + SDK (:SDK)
  |
  + DemoApp (:DemoApp)

Para la aplicación de demostración tenemos, fueron :SDK:jarjarDebugy :SDK:jarjarReleaseson algunas tareas específicas para las :SDKque producen algunos frascos posprocesados:

dependencies {
    debugCompile tasks.getByPath(":SDK:jarjarDebug").outputs.files
    releaseCompile tasks.getByPath(":SDK:jarjarRelease").outputs.files
    ... more dependencies ...
}

Esto funciona incluso para varios buildTypesconstruidos a la vez. Sin embargo, la depuración es un poco difícil. Por favor comenta.

javaj
fuente
1

Esta es mi solución: refleje BuildConfig del módulo de la aplicación:

`public static boolean debug = isDebug ();

private static boolean isDebug() {
    boolean result = false;
    try {
        Class c = Class.forName("com.example.app.BuildConfig");
        Field f = c.getField("DEBUG");
        f.setAccessible(true);
        result = f.getBoolean(c);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return result;
}`
Dalizhang
fuente
Ha utilizado la reflexión, pero esto no era necesario. Puede usar sabores en build.gradle.
Abhinav Saxena
0

Puedes probar esto en cada uno de los proyectos buildTypes:

parent.allprojects.each{ project -> android.defaultConfig.debuggable = true}
pablisco
fuente
¿Puede usted explicar por favor? ¿Añadirlo solo al buildType "depuración"? ¿Y a cada uno de los módulos? Me da un error: Error: (31, 0) No existe tal propiedad: depurable para la clase: com.android.build.gradle.internal.dsl.ProductFlavor_Decorated
desarrollador de Android
Las especificaciones del complemento gradle de Android han cambiado, por lo que ya no es válido. La marca depurable se ha movido a la buildTypeconfiguración de compilación y no a la de la compilación. La configuración teórica de la firma de depuración debería hacer el mismo truco
pablisco
¿Puedes revisarlo y actualizar la respuesta? Si hay una solución alternativa fácil, me gustaría conocerla.
desarrollador de Android
0

En mi caso, estaba importando mal BuildConfigya que mi proyecto tiene muchos módulos de biblioteca. La solución fue importar la correcta BuildConfigpara mi appmódulo.

Ryan R
fuente
0

Trabajando con verdadero depurable en archivo gradle.

buildTypes {
  demo{
 debuggable true
    }
  live{
 debuggable true
    }
}
Manikandan
fuente