BuildConfig.DEBUG luôn sai khi xây dựng các dự án thư viện với gradle


83

BuildConfig.DEBUG không hoạt động (= được đặt một cách hợp lý thành false) khi tôi chạy ứng dụng của mình ở chế độ gỡ lỗi. Tôi sử dụng Gradle để xây dựng. Tôi có một dự án thư viện, nơi tôi thực hiện kiểm tra này. BuildConfig.java trông giống như thế này trong thư mục gỡ lỗi xây dựng:

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

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

}

và trong thư mục phát hành:

public static final boolean DEBUG = false;

cả trong dự án thư viện và trong dự án ứng dụng.

Tôi đã cố gắng giải quyết vấn đề này bằng cách kiểm tra một biến được đặt một lớp cho dự án của tôi. Lớp này kế thừa từ thư viện và bắt đầu khi khởi động.

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

Điều này dẫn đến một vấn đề khác: đó là tôi sử dụng biến DEBUG của mình trong DataBaseProvider chạy trước lớp ứng dụng và nó sẽ không chạy đúng cách do lỗi này.


Đó là một hành vi bình thường. Vấn đề là ở đâu? Bạn cần phải chuyển qua lại giữa BuildVariants
Gabriele Mariotti

1
Tệp BuildConfig được tạo chính xác nhưng tại thời điểm chạy, nó là sai. Tôi gặp vấn đề tương tự.
jophde

Câu trả lời:


52

Đây là hành vi được mong đợi cho việc này.

Các dự án thư viện chỉ xuất bản các biến thể phát hành của chúng để các dự án hoặc mô-đun khác sử dụng.

Chúng tôi đang nỗ lực khắc phục sự cố này nhưng điều này không hề nhỏ và đòi hỏi một lượng lớn công việc.

Bạn có thể theo dõi sự cố tại https://code.google.com/p/android/issues/detail?id=52962


4
Cách giải quyết: đã cài đặt BuildConfig.DEBUG tạo một biến boolean khác tại lib-project, ví dụ như BuildConfig.RELEASE và liên kết nó với buildType của ứng dụng. Chi tiết: gist.github.com/almozavr/d59e770d2a6386061fcb
Oleksii Malovanyi

Giải pháp được cung cấp bởi DodoEnte trong trình theo dõi vấn đề hoạt động tốt, không cần phải làm việc xung quanh.
3c71

Đó không còn là trường hợp nữa. Có một giải pháp thích hợp cho điều đó. Xem câu trả lời của tôi để biết thêm thông tin.
Niklas

Điều đó đúng nhưng nó phải được làm thủ công và không cân bằng với hương vị. Chúng tôi muốn làm cho điều này tự động hơn trong tương lai.
Xavier Ducrohet

@XavierDucrohet Đây là một hành vi trực quan không mong đợi và ngược lại. Bạn chắc chắn nên cố gắng sửa chữa nó nếu bạn có thể.
Radu

86

Với Android Studio 1.1 và có cả phiên bản gradle ở 1.1, bạn có thể:

Thư viện

android {
    publishNonDefault true
}

Ứng dụng

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

Toàn bộ tài liệu có thể được tìm thấy tại đây http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication

CHỈNH SỬA :

Các vấn đề vừa được đánh dấu là cố định cho Android Studio Gradle Phiên bản 3.0. Ở đó bạn chỉ có thể sử dụng implementation project(path: ':library')và nó sẽ tự động chọn cấu hình chính xác.


5
Cách này hoạt động. Nhưng có một nhược điểm: ": library: assemblyRelease" được gọi ngay cả khi bạn đang tạo ": app: AssemblyDebug" và điều này sẽ dẫn đến thời gian xây dựng lâu hơn.
Alan Zhiliang Feng

Wow, cuối cùng họ đã cập nhật trang đó một chút và cuối cùng họ đã thêm tính năng này.
Jared Burrows

Cảm ơn, điều này đã làm công việc!
Aykut Çevik

@Konica Thời gian xây dựng Gradle lâu hơn là một cái giá nhỏ phải trả - dù sao thì nó cũng phức tạp và rất lâu !! Điều này làm việc tuyệt vời! Làm tốt!
Radu

Chúng tôi cần thêm phần "Ứng dụng" cho mỗi thư viện mà chúng tôi sử dụng? Nếu vậy, đó là khá khó chịu ...
nhà phát triển Android

46

Kiểm tra xem imports, đôi khi BuildConfig được nhập từ bất kỳ lớp thư viện nào không chủ ý. Ví dụ:

import io.fabric.sdk.android.BuildConfig;

Trong trường hợp này BuildConfig.DEBUG sẽ luôn trả về false ;

import com.yourpackagename.BuildConfig;

Trong trường hợp này BuildConfig.DEBUG sẽ trả về biến thể bản dựng thực của bạn .


8

Đây giống như câu trả lời của Phil ngoại trừ nó không cần bối cảnh:

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

Theo bài đăng trên blog này ( blog.javia.org/static-the-android-application-package ), bạn không bao giờ được gọi phương thức currentPackageName từ bất kỳ chuỗi nào khác ngoài chuỗi hoạt động (chuỗi giao diện người dùng). Giải pháp mát mẻ mặc dù.
Rolf ツ

@Rolf ツ Bạn có thể sử dụng ngữ cảnh ứng dụng thay thế.
nhà phát triển android

6

Để giải quyết vấn đề, bạn có thể sử dụng phương pháp này, sử dụng phản chiếu để nhận giá trị trường từ ứng dụng (không phải thư viện):

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

DEBUGVí dụ: để lấy trường, chỉ cần gọi trường này từ Activity:

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

Tôi cũng đã chia sẻ giải pháp này trên Trình theo dõi vấn đề AOSP .


@shkschneider dòng gì? Bạn có thể đăng ngoại lệ của bạn?
Phil

3
Có thể hữu ích cho những người khác: hãy cẩn thận với việc sử dụng applicationIdSuffixtrong Gradle sẽ khiến .BuildConfiglớp không thể truy cập được từ đoạn mã trên.
shkschneider

5

Không thực sự là cách chính xác để kiểm tra xem bạn có đang gỡ lỗi hay không, nhưng bạn có thể kiểm tra xem bản thân ứng dụng có thể gỡ lỗi hay không thông qua:

private static Boolean sIsDebuggable;

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

Hành vi mặc định của các ứng dụng và thư viện sẽ hoàn toàn khớp với nó.

Nếu bạn cần một giải pháp tốt hơn, bạn có thể sử dụng cách này thay thế:

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

Bạn có thể tạo lớp BuildConfig của riêng mình cho từng loại bản dựng bằng cách sử dụng gradle

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

cho /src/debug/.../MyBuildConfig.java và ...

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

cho /src/release/.../MyBuildConfig.java

Sau đó sử dụng:

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

Có "..." cho tên packageName của thư viện không? Nếu vậy, điều này có vẻ không hiệu quả. Tôi không thể truy cập lớp học.
nhà phát triển android

2

Đây là một giải pháp khác.

1) Tạo giao diện

public interface BuildVariantDetector {

    boolean isDebugVariant();

}

2) Sử dụng giao diện này trên lớp Ứng dụng (mô-đun Ứng dụng)

public class MyApplication extends Application implements BuildVariantDetector {

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

}

3) Và sau đó trong mô-đun thư viện:

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

Điều này không hoạt động. BuildConfig.DEBUG đối với tôi vẫn là sai.
DiscDev

Giải pháp đơn giản và thanh lịch. Chỉ cần đảm bảo rằng bạn nhập BuildConfig của mô-đun ứng dụng không phải của thư viện. Đó là một sai lầm rất lén lút.
WindRider

1

Chúng tôi đã có cùng một vấn đề. Tôi đã nghĩ ra một cái gì đó như thế này:

Chúng tôi có một SDK (thư viện) và một dự án demo, cấu trúc phân cấp trông như thế này:

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

Đối với ứng dụng demo, chúng tôi đã, đang :SDK:jarjarDebug:SDK:jarjarReleaselà một số nhiệm vụ cụ thể để :SDKtạo ra một số lọ sau xử lý:

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

Điều này hoạt động ngay cả với nhiều người buildTypesđược xây dựng cùng một lúc. Gỡ lỗi là một chút khó khăn mặc dù. Hãy bình luận.


1

Đây là cách giải quyết của tôi: phản ánh BuildConfig của mô-đun ứng dụng:

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

Bạn đã sử dụng phản xạ, nhưng điều này không cần thiết. Bạn có thể sử dụng các hương vị trong build.gradle.
Abhinav Saxena

0

Bạn có thể thử điều này trên từng dự án buildTypes:

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

Bạn có thể vui lòng giải thích? Chỉ thêm nó vào buildType "gỡ lỗi"? Và đối với từng mô-đun? Nó mang lại cho tôi một lỗi: Lỗi: (31, 0) Không có tài sản đó: debuggable cho lớp: com.android.build.gradle.internal.dsl.ProductFlavor_Decorated
nhà phát triển Android

Các thông số kỹ thuật của plugin android gradle đã thay đổi nên điều này không còn hợp lệ. Cờ có thể gỡ lỗi đã được chuyển đến buildTypechứ không phải cấu hình bản dựng. Tôi thiết lập các ký debug lý thuyết nên làm như vậy lừa
pablisco

Bạn có thể vui lòng xem thử và cập nhật câu trả lời được không? Nếu có một cách giải quyết dễ dàng, tôi muốn biết về nó.
nhà phát triển android

0

Trong trường hợp của tôi, tôi đã nhập sai BuildConfigvì dự án của tôi có nhiều mô-đun thư viện. Cách khắc phục là nhập đúng BuildConfigcho appmô-đun của tôi .


0

Làm việc với true có thể gỡ lỗi trong tệp gradle.

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.