BuildConfig.DEBUG zawsze ma wartość false podczas budowania projektów bibliotek za pomocą gradle

83

BuildConfig.DEBUG nie działa (= logicznie ustawiony na false), gdy uruchamiam aplikację w trybie debugowania. Używam Gradle do budowania. Mam projekt biblioteki, w którym robię to sprawdzenie. BuildConfig.java wygląda następująco w folderze debugowania kompilacji:

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

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

}

aw folderze wydania:

public static final boolean DEBUG = false;

zarówno w projekcie biblioteki, jak iw projekcie aplikacji.

Próbowałem to obejść, sprawdzając zmienną, która jest ustawiona na klasę mojego projektu. Ta klasa dziedziczy z biblioteki i jest uruchamiana podczas uruchamiania.

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

Prowadzi to do innego problemu: używam mojej zmiennej DEBUG w DataBaseProvider, która działa przed klasą aplikacji i nie będzie działać poprawnie z powodu tego błędu.

user1324936
źródło
To normalne zachowanie. Gdzie jest problem? Musisz przełączać się między BuildVariants
Gabriele Mariotti
1
Plik BuildConfig jest generowany poprawnie, ale w czasie wykonywania ma wartość false. Mam ten sam problem.
jophde

Odpowiedzi:

52

Jest to oczekiwane zachowanie w tym przypadku.

Projekty biblioteczne publikują tylko warianty wydania do wykorzystania przez inne projekty lub moduły.

Pracujemy nad rozwiązaniem tego problemu, ale nie jest to trywialne i wymaga znacznego nakładu pracy.

Możesz śledzić ten problem na https://code.google.com/p/android/issues/detail?id=52962

Xavier Ducrohet
źródło
4
Obejście: zamiast BuildConfig.DEBUG utwórz kolejną zmienną logiczną w lib-project, np. BuildConfig.RELEASE i połącz ją z typem buildType aplikacji. Szczegóły: gist.github.com/almozavr/d59e770d2a6386061fcb
Oleksii Malovanyi
Rozwiązanie dostarczone przez DodoEnte w narzędziu do śledzenia problemów działa dobrze, nie ma potrzeby wykonywania obejścia.
3c71
Tak już nie jest. Jest na to właściwe rozwiązanie. Zobacz moją odpowiedź, aby uzyskać więcej informacji.
Niklas
To prawda, ale trzeba to zrobić ręcznie i nie skaluje się dobrze ze smakami. Chcemy, aby w przyszłości było to bardziej automatyczne.
Xavier Ducrohet
@XavierDucrohet Jest to nieoczekiwane i sprzeczne z intuicją zachowanie. Zdecydowanie powinieneś spróbować to naprawić, jeśli możesz.
Radu
86

Dzięki Android Studio 1.1 i mając również wersję Gradle w wersji 1.1 jest możliwe:

Biblioteka

android {
    publishNonDefault true
}

Aplikacja

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

Pełną dokumentację można znaleźć tutaj http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

EDYCJA :

Problem właśnie został oznaczony jako stałe dla Androida Studio Gradle wersji 3.0. Tam możesz po prostu użyć, implementation project(path: ':library')a automatycznie wybierze prawidłową konfigurację.

Niklas
źródło
5
Tak działa. Ale jest wada: ": library: assembleRelease" jest wywoływana nawet wtedy, gdy tworzysz ": app: assembleDebug", co spowoduje wydłużenie czasu kompilacji.
Alan Zhiliang Feng
Wow, w końcu trochę zaktualizowali tę stronę i wreszcie dodali tę funkcję.
Jared Burrows
Dzięki, to zadziałało!
Aykut Çevik
@Konica Dłuższy czas budowy Gradle to niewielka cena - i tak jest zawiły i długi czas !! To zadziałało cudownie! Dobra robota!
Radu
Musimy dodać część „App” dla każdej używanej biblioteki? Jeśli tak, to dość denerwujące ...
programista Androida
47

Sprawdź imports, czy czasami BuildConfig jest importowany z dowolnej klasy biblioteki. Na przykład:

import io.fabric.sdk.android.BuildConfig;

W tym przypadku BuildConfig.DEBUG zawsze zwróci false ;

import com.yourpackagename.BuildConfig;

W tym przypadku BuildConfig.DEBUG zwróci Twój prawdziwy wariant kompilacji.

Gent Berani
źródło
8

To jest jak odpowiedź Phila, ale nie potrzebuje kontekstu:

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
źródło
Zgodnie z tym postem na blogu ( blog.javia.org/static-the-android-application-package ) nigdy nie należy wywoływać metody currentPackageName z żadnego innego wątku niż wątek aktywności (wątek interfejsu użytkownika). Ale fajne rozwiązanie.
Rolf ツ
@Rolf ツ Zamiast tego możesz użyć kontekstu aplikacji.
programista Androida
6

Aby obejść ten problem, możesz użyć tej metody, która używa odbicia w celu pobrania wartości pola z aplikacji (nie z biblioteki):

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

Aby uzyskać DEBUG przykład, pole, po prostu zadzwoń do niego ze swojego Activity:

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

Udostępniłem również to rozwiązanie w narzędziu do śledzenia problemów AOSP .

Phil
źródło
@shkschneider what line? Czy możesz opublikować swój wyjątek?
Phil
3
Może być przydatny dla innych: uważaj na użycie applicationIdSuffixw Gradle, które spowodowałoby, że .BuildConfigklasa nie byłaby dostępna z powyższego kodu.
shkschneider
5

Nie jest to właściwy sposób sprawdzenia, czy jesteś w fazie debugowania, ale możesz sprawdzić, czy sama aplikacja jest debugowalna przez:

private static Boolean sIsDebuggable;

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

Domyślne zachowanie aplikacji i bibliotek będzie idealnie do niego pasować.

Jeśli potrzebujesz lepszego obejścia, możesz użyć tego:

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;
}
programista Androida
źródło
2

Możesz utworzyć własną klasę BuildConfig dla każdego typu kompilacji za pomocą gradle

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

dla /src/debug/.../MyBuildConfig.java i ...

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

for /src/release/.../MyBuildConfig.java

Następnie użyj:

if (MyBuildConfig.DEBUG)
    Log.d(TAG, "Hey! This is debug version!");
Grupa
źródło
Czy „...” dla nazwy pakietu biblioteki? Jeśli tak, to nie działa. Nie mam dostępu do zajęć.
programista Androida
2

Oto inne rozwiązanie.

1) Utwórz interfejs

public interface BuildVariantDetector {

    boolean isDebugVariant();

}

2) Użyj tego interfejsu w klasie aplikacji (moduł aplikacji)

public class MyApplication extends Application implements BuildVariantDetector {

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

}

3) A następnie w module biblioteki:

boolean debugVariant = ((BuildVariantDetector)getApplication()).isDebugVariant();
Dominik Suszczewicz
źródło
To nie działa. BuildConfig.DEBUG nadal jest dla mnie fałszywy.
DiscDev
Proste i eleganckie rozwiązanie. Upewnij się tylko, że importujesz BuildConfig modułu aplikacji, a nie bibliotekę. To bardzo podstępny błąd.
WindRider,
1

Mieliśmy ten sam problem. Wymyśliłem coś takiego:

Mamy SDK (bibliotekę) i projekt demonstracyjny, hierarchia wygląda tak:

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

W przypadku aplikacji demonstracyjnej mamy, byliśmy :SDK:jarjarDebugi mamy :SDK:jarjarReleasekilka konkretnych zadań do :SDKprodukcji niektórych słoików po przetworzeniu:

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

Działa to nawet w przypadku wielu buildTypesbudowanych jednocześnie. Debugowanie jest jednak nieco trudne. Proszę skomentuj.

javaj
źródło
1

To jest moje obejście: odzwierciedlenie BuildConfig modułu aplikacji:

`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
źródło
Użyłeś refleksji, ale nie było to konieczne. Możesz użyć smaków w pliku build.gradle.
Abhinav Saxena
0

Możesz wypróbować to na każdym z projektów buildTypes:

parent.allprojects.each{ project -> android.defaultConfig.debuggable = true}
pablisco
źródło
Czy możesz wyjaśnić? Dodać go tylko do „debugowania” buildType? A do każdego z modułów? Daje mi błąd: Błąd: (31, 0) Brak takiej właściwości: debugowalna dla klasy: com.android.build.gradle.internal.dsl.ProductFlavor_Decorated
programista Androida
Specyfikacje wtyczki Gradle dla Androida uległy zmianie, więc nie jest to już ważne. Flaga z możliwością debugowania została przeniesiona do, buildTypea nie do konfiguracji kompilacji. Teoretycznie ustawienie podpisywania debugowania powinno zrobić tę samą sztuczkę
pablisco
Czy możesz to sprawdzić i zaktualizować odpowiedź? Jeśli istnieje łatwe obejście, chciałbym się o tym dowiedzieć.
programista Androida
0

W moim przypadku źle importowałem, BuildConfigponieważ mój projekt ma wiele modułów bibliotecznych. Poprawka polegała na zaimportowaniu poprawnego BuildConfigdla mojego appmodułu.

Ryan R.
źródło
0

Praca z debugowalnym true w pliku gradle.

buildTypes {
  demo{
 debuggable true
    }
  live{
 debuggable true
    }
}
Manikandan
źródło
0

BuildConfig.DEBUG nie jest w ogóle niezawodny, system Android dostarczył wewnętrzną flagę, która jest globalnie dostępna, wskazując, czy kompilacja jest w trybie debugowania, czy innym

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

będzie prawdziwe, jeśli jest w trakcie debugowania

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

Eldhopj
źródło