BuildConfig.DEBUG завжди хибний при створенні бібліотечних проектів за допомогою gradle


83

BuildConfig.DEBUG не працює (= логічно встановлено як false), коли я запускаю свою програму в режимі налагодження. Я використовую Gradle для побудови. У мене є бібліотечний проект, де я виконую цю перевірку. BuildConfig.java виглядає так у папці налагодження збірки:

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

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

}

і в папці випуску:

public static final boolean DEBUG = false;

як у проекті бібліотеки, так і в проекті програми.

Я намагався обійти це, перевіривши змінну, яка встановлює клас мого проекту. Цей клас успадковується від бібліотеки та запускається під час запуску.

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

Це призводить до іншої проблеми: це те, що я використовую свою змінну DEBUG у DataBaseProvider, який працює перед класом програми, і вона не буде працювати належним чином через цю помилку.


Це нормальна поведінка. Де проблема? Ви повинні перемикатися між BuildVariants
Gabriele Mariotti

1
Файл BuildConfig генерується правильно, але під час виконання він є помилковим. У мене така ж проблема.
jophde

Відповіді:


52

Це очікувана поведінка для цього.

Бібліотечні проекти публікують лише свої варіанти випуску для споживання іншими проектами або модулями.

Ми працюємо над виправленням цього, але це нетривіально і вимагає значного обсягу роботи.

Ви можете відстежити проблему за адресою https://code.google.com/p/android/issues/detail?id=52962


4
Вирішення проблеми: встановлено BuildConfig.DEBUG, створити ще одну логічну змінну в проекті lib, наприклад, BuildConfig.RELEASE, та зв’язати її з buildType програми. Подробиці: gist.github.com/almozavr/d59e770d2a6386061fcb
Олексій Мальований

Рішення, яке надає DodoEnte у програмі відстеження випусків, працює чудово, немає необхідності в обігу.
3c71,

Це вже не так. Для цього є відповідне рішення. Докладнішу інформацію див. У моїй відповіді .
Ніклас

Це правда, але це потрібно робити вручну, і воно не дуже добре масштабується зі смаками. Ми хочемо зробити це більш автоматичним у майбутньому.
Xavier Ducrohet

@XavierDucrohet Це несподівана та протилежна інтуїтивна поведінка. Ви обов’язково повинні спробувати це виправити, якщо можете.
Раду

86

З Android Studio 1.1 та версією gradle на 1.1 можна:

Бібліотека

android {
    publishNonDefault true
}

Додаток

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

Повну документацію можна знайти тут http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

РЕДАГУВАТИ :

Питання тільки що був відзначений як фіксований для Android Studio Gradle версії 3.0. Там ви можете просто скористатися, implementation project(path: ':library')і він автоматично вибере правильну конфігурацію.


5
Цей спосіб працює. Але є недолік: ": library: assembleRelease" викликається навіть через те, що ви робите ": app: assembleDebug", і це призведе до збільшення часу нарощування.
Алан Жиліан Фенг

Ого, нарешті вони трохи оновили цю сторінку і нарешті додали цю функцію.
Джаред Берроус,

Дякую, це зробило роботу!
Айкут Чевік

@Konica Longer Gradle - час збірки - це невелика ціна, яку потрібно заплатити - вона все одно заплутана і довга! Це спрацювало чудово! Молодці!
Раду

Нам потрібно додати частину "App" для кожної бібліотеки, якою ми користуємося? Якщо так, це досить дратує ...
розробник android

46

Перевірте imports, іноді BuildConfig ненавмисно імпортується з будь-якого класу бібліотеки. Наприклад:

import io.fabric.sdk.android.BuildConfig;

У цьому випадку BuildConfig.DEBUG завжди повертає false ;

import com.yourpackagename.BuildConfig;

У цьому випадку BuildConfig.DEBUG поверне ваш реальний варіант збірки.


8

Це як відповідь Філа, за винятком того, що йому не потрібен контекст:

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

Відповідно до цього ( blog.javia.org/static-the-android-application-package ) повідомлення в блозі, ви ніколи не повинні викликати метод currentPackageName з будь-якого потоку, крім потоку активності (потік інтерфейсу користувача). Хоча прохолодне рішення.
Рольф ツ

@Rolf ツ Ну, замість цього ви можете використовувати контекст програми.
розробник android

6

Як обхідний шлях можна використовувати цей метод, який використовує відображення, щоб отримати значення поля з програми (а не з бібліотеки):

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

DEBUGНаприклад, щоб отримати поле, просто зателефонуйте йому зі свого Activity:

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

Я також поділився цим рішенням у програмі відстеження випусків AOSP .


@shkschneider який рядок? Чи можете ви опублікувати свій виняток?
Філ

3
Може бути корисним для інших: остерігайтеся використання applicationIdSuffixв Gradle, яке зробить .BuildConfigклас недоступним із цього вище коду.
shkschneider

5

Не дуже правильний спосіб перевірити, чи є у вас настрій для налагодження, але ви можете перевірити, чи не відладжується сама програма за допомогою:

private static Boolean sIsDebuggable;

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

Поведінка програм і бібліотек за замовчуванням буде ідеально відповідати йому.

Якщо вам потрібен кращий обхідний шлях, ви можете використовувати замість цього:

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

Ви можете створити власний клас BuildConfig для кожного типу збірки за допомогою gradle

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

для /src/debug/.../MyBuildConfig.java та ...

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

для /src/release/.../MyBuildConfig.java

Потім використовуйте:

if (MyBuildConfig.DEBUG)
    Log.d(TAG, "Hey! This is debug version!");

Чи "..." для packageName бібліотеки? Якщо так, це, здається, не працює. Я не можу отримати доступ до класу.
розробник android

2

Ось ще одне рішення.

1) Створити інтерфейс

public interface BuildVariantDetector {

    boolean isDebugVariant();

}

2) Використовуйте цей інтерфейс у класі Application (модуль Appplication)

public class MyApplication extends Application implements BuildVariantDetector {

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

}

3) А далі в бібліотечному модулі:

boolean debugVariant = ((BuildVariantDetector)getApplication()).isDebugVariant();

Це не працює. BuildConfig.DEBUG все ще є помилковим для мене.
DiscDev

Просте та елегантне рішення. Просто переконайтеся, що ви імпортуєте модуль програми BuildConfig, а не бібліотеку. Це дуже підла помилка.
WindRider

1

У нас була та сама проблема. Я придумав щось подібне:

У нас є SDK (бібліотека) та демонстраційний проект, ієрархія виглядає так:

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

Для демонстраційного додатка, який ми маємо, були :SDK:jarjarDebugі :SDK:jarjarReleaseє деякі конкретні завдання, :SDKякі створюють кілька пост-оброблених банок:

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

Це працює навіть для декількох buildTypesпобудованих одночасно. Хоча налагодження дещо складне. Будь ласка, коментуйте.


1

Це мій спосіб вирішити: відображати BuildConfig модуля програми:

`загальнодоступна статична логічна налагодження = 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;
}`

Ви використовували роздуми, але в цьому не було необхідності. Ви можете використовувати ароматизатори в build.gradle.
Абхінав Саксена

0

Ви можете спробувати це на кожному з проектів buildTypes:

parent.allprojects.each{ project -> android.defaultConfig.debuggable = true}

Ви можете пояснити? Додати його лише до типу налагодження "buildType"? А до кожного з модулів? Це видає мені помилку: Помилка: (31, 0) Немає такої властивості: налагоджується для класу: com.android.build.gradle.internal.dsl.ProductFlavor_Decorated
розробник Android

Характеристики плагіна android gradle змінилися, тому це більше не діє. Прапор, що налагоджується, переміщено до buildTypeконфігурації збірки, а не до конфігурації. Я вважаю, що підписання налагодження має зробити той самий трюк
pablisco

Чи можете ви перевірити це і оновити відповідь? Якщо є легкий спосіб вирішення проблеми, я хотів би про це знати.
розробник android

0

У моєму випадку я імпортував неправильно, BuildConfigоскільки мій проект має багато бібліотечних модулів. Виправлення полягало в імпорті правильного BuildConfigдля мого appмодуля.



0

BuildConfig.DEBUG взагалі не є надійним, Android забезпечив внутрішній прапор, який доступний у всьому світі, який вказує, чи є збірка в режимі налагодження чи без налагодження

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

буде правдою, якщо вона знаходиться в налагодженні

Подяки: https://medium.com/@elye.project/checking-debug-build-the-right-way-d12da1098120

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.