BuildConfig.DEBUG ist immer falsch, wenn Bibliotheksprojekte mit gradle erstellt werden

83

BuildConfig.DEBUG funktioniert nicht (= logisch auf false gesetzt), wenn ich meine App im Debug-Modus ausführe. Ich benutze Gradle zum Bauen. Ich habe ein Bibliotheksprojekt, in dem ich diese Prüfung durchführe. BuildConfig.java sieht im Build-Debug-Ordner folgendermaßen aus:

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

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

}

und im Release-Ordner:

public static final boolean DEBUG = false;

sowohl im Bibliotheksprojekt als auch im Anwendungsprojekt.

Ich habe versucht, dies zu umgehen, indem ich eine Variable überprüft habe, die eine Klasse meines Projekts ist. Diese Klasse erbt von der Bibliothek und startet beim Start.

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

Dies führt zu einem weiteren Problem: Ich verwende meine DEBUG-Variable in einem DataBaseProvider, der vor der Anwendungsklasse ausgeführt wird, und sie wird aufgrund dieses Fehlers nicht ordnungsgemäß ausgeführt.

user1324936
quelle
Es ist ein normales Verhalten. Wo ist das Problem? Sie müssen zwischen BuildVariants
Gabriele Mariotti
1
Die BuildConfig-Datei wird korrekt generiert, ist jedoch zur Laufzeit falsch. Ich habe das gleiche Problem.
Jophde

Antworten:

52

Dies ist das erwartete Verhalten dafür.

Bibliotheksprojekte veröffentlichen ihre Release-Varianten nur zum Verbrauch durch andere Projekte oder Module.

Wir arbeiten daran, dies zu beheben, aber dies ist nicht trivial und erfordert einen erheblichen Arbeitsaufwand.

Sie können das Problem unter https://code.google.com/p/android/issues/detail?id=52962 verfolgen

Xavier Ducrohet
quelle
4
Problemumgehung: Mit BuildConfig.DEBUG wird eine weitere boolesche Variable bei lib-project erstellt, z. B. BuildConfig.RELEASE, und mit dem buildType der Anwendung verknüpft. Details: gist.github.com/almozavr/d59e770d2a6386061fcb
Oleksii Malovanyi
Die von DodoEnte im Issue Tracker bereitgestellte Lösung funktioniert einwandfrei, ohne dass eine Umgehung erforderlich ist.
3c71
Das ist nicht mehr der Fall. Dafür gibt es eine richtige Lösung. Weitere Informationen finden Sie in meiner Antwort .
Niklas
Das stimmt, aber es muss manuell gemacht werden und lässt sich nicht sehr gut mit Aromen skalieren. Wir wollen dies in Zukunft automatisieren.
Xavier Ducrohet
@XavierDucrohet Dies ist ein unerwartetes und kontraintuitives Verhalten. Sie sollten auf jeden Fall versuchen, das Problem zu beheben, wenn Sie können.
Radu
86

Mit Android Studio 1.1 und der Gradle-Version 1.1 ist Folgendes möglich:

Bibliothek

android {
    publishNonDefault true
}

App

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

Die vollständige Dokumentation finden Sie hier http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

EDIT :

Das Problem wurde gerade für Android Studio Gradle Version 3.0 als behoben markiert. Dort können Sie einfach verwenden implementation project(path: ':library')und es wird automatisch die richtige Konfiguration ausgewählt.

Niklas
quelle
5
Dieser Weg funktioniert. Es gibt jedoch einen Nachteil: Die ": library: assembleRelease" wird aufgerufen, obwohl Sie die ": app: assembleDebug" erstellen. Dies führt zu einer längeren Bauzeit.
Alan Zhiliang Feng
Wow, sie haben diese Seite endlich ein wenig aktualisiert und diese Funktion endlich hinzugefügt.
Jared Burrows
Danke, das hat den Job gemacht!
Aykut Çevik
@Konica Longer Gradle Build Time ist ein kleiner Preis - es ist kompliziert und sowieso lange Zeiten !! Das hat wunderbar funktioniert! Gut gemacht!
Radu
Müssen wir den "App" -Teil für jede Bibliothek hinzufügen, die wir verwenden? Wenn ja, ist das ziemlich ärgerlich ...
Android-Entwickler
46

Überprüfen Sie imports, ob BuildConfig manchmal unbeabsichtigt aus einer beliebigen Bibliotheksklasse importiert wird. Beispielsweise:

import io.fabric.sdk.android.BuildConfig;

In diesem Fall gibt BuildConfig.DEBUG immer false zurück .

import com.yourpackagename.BuildConfig;

In diesem Fall gibt BuildConfig.DEBUG Ihre echte Build-Variante zurück.

Gent Berani
quelle
8

Dies ist wie Phils Antwort, außer dass der Kontext nicht benötigt wird:

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
quelle
Gemäß diesem Blog-Beitrag ( blog.javia.org/static-the-android-application-package ) sollten Sie die currentPackageName-Methode niemals von einem anderen Thread als dem Aktivitätsthread (UI-Thread) aus aufrufen. Coole Lösung.
Rolf
@Rolf ツ Nun, du könntest stattdessen den Anwendungskontext verwenden.
Android-Entwickler
6

Um dieses Problem zu umgehen, können Sie diese Methode verwenden, bei der mithilfe von Reflektion der Feldwert aus der App (nicht aus der Bibliothek) abgerufen wird:

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

Um das DEBUGFeld zu erhalten, rufen Sie zum Beispiel einfach Folgendes auf Activity:

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

Ich habe diese Lösung auch auf dem AOSP Issue Tracker geteilt .

Phil
quelle
@shkschneider welche Zeile? Können Sie Ihre Ausnahme posten?
Phil
3
Könnte für andere nützlich sein: Achten Sie auf die Verwendung von applicationIdSuffixin Gradle, die die .BuildConfigKlasse von diesem obigen Code aus nicht erreichbar machen würde .
Shkschneider
5

Nicht wirklich die richtige Methode, um zu überprüfen, ob Sie sich im Debug-Stil befinden, aber Sie können überprüfen, ob die App selbst über Folgendes debuggbar ist:

private static Boolean sIsDebuggable;

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

Das Standardverhalten von Apps und Bibliotheken passt perfekt dazu.

Wenn Sie eine bessere Problemumgehung benötigen, können Sie diese stattdessen verwenden:

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;
}
Android-Entwickler
quelle
2

Mit gradle können Sie für jeden Build-Typ eine eigene BuildConfig-Klasse erstellen

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

für /src/debug/.../MyBuildConfig.java und ...

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

für /src/release/.../MyBuildConfig.java

Dann benutze:

if (MyBuildConfig.DEBUG)
    Log.d(TAG, "Hey! This is debug version!");
Cluster
quelle
Entspricht "..." dem Paketnamen der Bibliothek? Wenn ja, scheint dies nicht zu funktionieren. Ich kann nicht auf die Klasse zugreifen.
Android-Entwickler
2

Hier ist eine andere Lösung.

1) Erstellen Sie eine Schnittstelle

public interface BuildVariantDetector {

    boolean isDebugVariant();

}

2) Verwenden Sie diese Schnittstelle für die Anwendungsklasse (Anwendungsmodul).

public class MyApplication extends Application implements BuildVariantDetector {

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

}

3) Und dann im Bibliotheksmodul:

boolean debugVariant = ((BuildVariantDetector)getApplication()).isDebugVariant();
Dominik Suszczewicz
quelle
Das funktioniert nicht. BuildConfig.DEBUG ist für mich immer noch falsch.
DiscDev
Einfache und elegante Lösung. Stellen Sie einfach sicher, dass Sie die BuildConfig des App-Moduls importieren, nicht die der Bibliothek. Das ist ein sehr hinterhältiger Fehler.
WindRider
1

Wir hatten das gleiche Problem. Ich habe mir so etwas ausgedacht:

Wir haben ein SDK (Bibliothek) und ein Demo-Projekt. Die Hierarchie sieht folgendermaßen aus:

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

Für die Demo-App, die wir haben, waren :SDK:jarjarDebugund :SDK:jarjarReleasesind einige spezifische Aufgaben :SDK, die einige nachbearbeitete Gläser produzieren:

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

Dies funktioniert auch für mehrere buildTypesgleichzeitig erstellte. Das Debuggen ist allerdings etwas schwierig. Bitte kommentieren.

javaj
quelle
1

Dies ist meine Problemumgehung: Reflect BuildConfig des App-Moduls:

`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
quelle
Sie haben Reflexion verwendet, aber dies war nicht notwendig. Sie können Aromen in der build.gradle verwenden.
Abhinav Saxena
0

Sie können dies bei jedem der BuildTypes-Projekte versuchen:

parent.allprojects.each{ project -> android.defaultConfig.debuggable = true}
Pablisco
quelle
Kannst du bitte Erklären? Nur zum "debug" buildType hinzufügen? Und zu jedem der Module? Es gibt mir einen Fehler: Fehler: (31, 0) Keine solche Eigenschaft: debuggable für Klasse: com.android.build.gradle.internal.dsl.ProductFlavor_Decorated
Android-Entwickler
Die Spezifikationen des Android Gradle Plugins wurden geändert, sodass dies nicht mehr gültig ist. Das debuggable-Flag wurde in die buildTypeund nicht in die Build-Konfiguration verschoben . Ich theoretisch Einstellung der Debug-Signatur sollte den gleichen Trick tun
Pablisco
Können Sie es bitte überprüfen und die Antwort aktualisieren? Wenn es eine einfache Problemumgehung gibt, würde ich gerne davon erfahren.
Android-Entwickler
0

In meinem Fall habe ich das falsche importiert, BuildConfigda mein Projekt viele Bibliotheksmodule enthält. Das Update bestand darin, das richtige BuildConfigfür mein appModul zu importieren .

Ryan R.
quelle
0

Arbeiten mit debuggable true in Gradle-Datei.

buildTypes {
  demo{
 debuggable true
    }
  live{
 debuggable true
    }
}
Manikandan
quelle
0

BuildConfig.DEBUG ist überhaupt nicht zuverlässig. Android hat ein intern verfügbares internes Flag bereitgestellt, das angibt, ob sich ein Build im Debug- oder Nicht-Debug-Modus befindet

(getContext().getApplicationInfo().flags &ApplicationInfo.FLAG_DEBUGGABLE) != 0) 

wird wahr sein, wenn es im Debug ist

Credits: https://medium.com/@elye.project/checking-debug-build-the-right-way-d12da1098120

Eldhopj
quelle