63

私の(gradle 1.10およびgradle plugin 0.8)ベースのAndroidプロジェクトは、3つの異なるAndroidアプリの依存関係である大きなAndroidライブラリで構成されています

私のライブラリでは、このような構造を使用できるようにしたいと考えています

if (BuildConfig.SOME_FLAG) {
    callToBigLibraries()
}

プロガードは、SOME_FLAG の最終値に基づいて、生成された apk のサイズを縮小できるため

しかし、gradle as でそれを行う方法がわかりません:

* the BuildConfig produced by the library doesn't have the same package name than the app
* I have to import the BuildConfig with the library package in the library
* The apk of an apps includes the BuildConfig with the package of the app but not the one with the package of the library.

BuildTypes などで遊んでみましたが成功しませんでした

release {
    // packageNameSuffix "library"
    buildConfigField "boolean", "SOME_FLAG", "true"
}
debug {
    //packageNameSuffix "library"
    buildConfigField "boolean", "SOME_FLAG", "true"
}

アプリのビルド時にフラグがオーバーライドされるライブラリとアプリの共有 BuildConfig をビルドする正しい方法は何ですか?

4

7 に答える 7

50

回避策として、リフレクションを使用して (ライブラリではなく) アプリからフィールド値を取得するこのメソッドを使用できます。

/**
 * 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 Issue Trackerで共有しました。

于 2014-08-12T14:34:06.600 に答える
36

更新: Android Gradle プラグインの新しいバージョンでpublishNonDefaultは廃止され、効果がなくなりました。すべてのバリアントが公開されました。

次の解決策/回避策は私にとってはうまくいきます。これは、Google イシュー トラッカーの一部の人物によって投稿されました。

ライブラリプロジェクトで次のように設定publishNonDefaultしてみてください。true

android {
    ...
    publishNonDefault true
    ...
}

そして、ライブラリを使用しているアプリプロジェクトに次の依存関係を追加します。

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

このようにして、ライブラリを使用するプロジェクトには、ライブラリの正しいビルド タイプが含まれます。

于 2015-07-16T14:46:01.303 に答える
22

BuildConfig.SOME_FLAGライブラリに適切に伝播されないため、やりたいことができません。ビルド タイプ自体はライブラリに反映されません。常に RELEASE としてビルドされます。これはバグhttps://code.google.com/p/android/issues/detail?id=52962です

これを回避するには: すべてのライブラリ モジュールを制御できる場合、触れるすべてのコードがcallToBigLibraries()、ProGuard できれいに切断できるクラスとパッケージにあることを確認してから、リフレクションを使用して、次の場合にそれらにアクセスできるようにすることができます。それらは存在し、存在しない場合は正常に劣化します。基本的に同じことを行っていますが、コンパイル時ではなく実行時にチェックを行っているため、少し難しくなります。

これを行う方法がわからない場合はお知らせください。必要であればサンプルを提供できます。

于 2014-01-29T00:25:42.540 に答える
5

applicationId がパッケージと同じではない場合 (つまり、プロジェクトごとに複数の applicationId がある場合) で、かつライブラリ プロジェクトからアクセスする場合:

Gradle を使用して、ベース パッケージをリソースに格納します。

main/AndroidManifest.xml 内:

android {
    applicationId "com.company.myappbase"
    // note: using ${applicationId} here will be exactly as above
    // and so NOT necessarily the applicationId of the generated APK
    resValue "string", "build_config_package", "${applicationId}"
}

Java の場合:

public static boolean getDebug(Context context) {
    Object obj = getBuildConfigValue("DEBUG", context);
    if (obj instanceof Boolean) {
        return (Boolean) o;
    } else {
        return false;
    }
}

private static Object getBuildConfigValue(String fieldName, Context context) {
    int resId = context.getResources().getIdentifier("build_config_package", "string", context.getPackageName());
    // try/catch blah blah
    Class<?> clazz = Class.forName(context.getString(resId) + ".BuildConfig");
    Field field = clazz.getField(fieldName);
    return field.get(null);
}
于 2015-07-23T12:47:22.720 に答える
1

両方を使う

my build.gradle
// ...
productFlavors {
    internal {
        // applicationId "com.elevensein.sein.internal"
        applicationIdSuffix ".internal"
        resValue "string", "build_config_package", "com.elevensein.sein"
    }

    production {
        applicationId "com.elevensein.sein"
    }
}

以下のように呼び出したい

Boolean isDebug = (Boolean) BuildConfigUtils.getBuildConfigValue(context, "DEBUG");

BuildConfigUtils.java

public class BuildConfigUtils
{

    public static Object getBuildConfigValue (Context context, String fieldName)
    {
        Class<?> buildConfigClass = resolveBuildConfigClass(context);
        return getStaticFieldValue(buildConfigClass, fieldName);
    }

    public static Class<?> resolveBuildConfigClass (Context context)
    {
        int resId = context.getResources().getIdentifier("build_config_package",
                                                         "string",
                                                         context.getPackageName());
        if (resId != 0)
        {
            // defined in build.gradle
            return loadClass(context.getString(resId) + ".BuildConfig");
        }

        // not defined in build.gradle
        // try packageName + ".BuildConfig"
        return loadClass(context.getPackageName() + ".BuildConfig");

    }

    private static Class<?> loadClass (String className)
    {
        Log.i("BuildConfigUtils", "try class load : " + className);
        try { 
            return Class.forName(className); 
        } catch (ClassNotFoundException e) { 
            e.printStackTrace(); 
        }

        return null;
    }

    private static Object getStaticFieldValue (Class<?> clazz, String fieldName)
    {
        try { return clazz.getField(fieldName).get(null); }
        catch (NoSuchFieldException e) { e.printStackTrace(); }
        catch (IllegalAccessException e) { e.printStackTrace(); }
        return null;
    }
}
于 2016-02-03T16:27:15.153 に答える