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.


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


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


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.


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

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 .


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

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!");

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();

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.


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

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}

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.


0

Praca z debugowalnym true w pliku gradle.

buildTypes {
  demo{
 debuggable true
    }
  live{
 debuggable true
    }
}

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.