buildConfig上の値の直接参照について

はじめに

  • release.apk でstoreにアップ済みのapkに対して、アップグレードテストをしたい
  • でも一部の処理は処理ログとかで動作も確認したい

な用途で毎回コード上の定義値を変更してビルドするのも面倒なので、

buildConfigFieldあたりの挙動を調べてみた

現在のBuildConfigの状態

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String APPLICATION_ID = "hoge.fuga.irof_history";
  public static final String BUILD_TYPE = "debug";
  public static final String FLAVOR = ""; //★
  public static final int VERSION_CODE = 1;
  public static final String VERSION_NAME = "1.0";

  //buildConfigFieldで指定されたものが追加
}

★ がandroid gradle pluginで新規追加されたもので、

  • BuildConfig.FLAVORを使ったライブラリ、コードが存在

みたいな凶悪な移行推奨ステマが昔流行った時期がありました・・

独自BuildConfig変数の追加というと

下記なサンプルがよくでてきます。

buildTypes {
    debug {
      buildConfigField "Boolean", "DEBUG_LOG", 'true'
    }

    release {
      buildConfigField "Boolean", "DEBUG_LOG", 'false'
    }

    releaseOut.initWith(buildTypes.release)
    releaseOut {
      buildConfigField "Boolean", "DEBUG_LOG", 'true'
    }
}

or

  • app/build.gradle
defaultConfig{
      buildConfigField "Boolean", "DEBUG_LOG", 'false'
}

buildTypes {
    debug {
    }

    release {
    }

    releaseOut.initWith(buildTypes.release)
    releaseOut {
      buildConfigField "Boolean", "DEBUG_LOG", 'true'
    }
}

でもこれすごく微妙。どうせ書くなら使うところだけ書きたい

  • app/build.gradle
buildTypes {
    debug {
    }

    release {
    }

    releaseOut.initWith(buildTypes.release)
    releaseOut {
      buildConfigField "Boolean", "DEBUG_LOG", 'true'
    }
}

この場合、動的に参照したくなるわけです

BuildConfigというと

<<アプリパッケージ名>>.BuildConfig.java として生成されます

したがってこのクラスを動的に参照できればいいわけです

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) {
    } catch (NoSuchFieldException e) {
    } catch (IllegalAccessException e) {
    }
    return null;
}
boolean debugLog = (Boolean) getBuildConfigValue(getApplicationContext(), "DEBUG_LOG");//★

なコードがStackOverFlowで出てきますが、

  • ★の第1引数は getApplicationContext() でないと<<アプリケーションパッケージ名>> はとれません*1
    • 多分参考元の想定は、下記なんだろうな・・と
      • カスタムApplication上に記載
      • カスタムApplicationはプロジェクト直下
  • 例えば 下記の場合は hoge.fuga.util.BuildConfig.java のクラスを参照しようとしてしまいます
package hoge.fuga.util


class hogeActivity extends Activity{

    void onCreate(){
        boolean debugLog = (Boolean) getBuildConfigValue(this, "DEBUG_LOG");//◎
    }
}

でもまあ app本体側のパッケージの位置さえわかればいいのですから、下記のほうがシンプルかも・・

参考元の趣旨と外れるかもしれませんが・・

public static Object getBuildConfigValue(String fieldName) {
    try {
        Field field = BuildConfig.class.getField(fieldName);
        return field.get(null);
    } catch (ClassNotFoundException e) {
    } catch (NoSuchFieldException e) {
    } catch (IllegalAccessException e) {
    }
    return null;
}

これだけではもちろん足りなくて

  • proguard-rules.pro
 -keep public class hoge.fuga.BuildConfig { *; }

指定しないとBuildConfigクラス自体が生成されない*2

の指定は必要。support-v4とかも、JD-GUIとかで見ると

実は BuildConfigはそのまま残ってるんですよね・・

参考元

参考元の話は、

aar化した libraryProjectでどう遣ったら本体側の BuildConfig.DEBUGと連動できるか? の話なんですけど、

下手に難しいことするより、自分的所感としては昔のjar版みたいに、setDebugMode みたいな関数を作って渡せば良くない?

とか思って読んでました。そもそもココらへんの話って,

gradlew assembleDebug

しても ソース同梱しているはずのlibraryProject側のaarがrelease.aarがくっつくからログが出ない

というG様のよくわかんない挙動仕様によるものだったり・・

  • ソース同梱型のlibraryProject側の指定が
    • AS上のbuildType指定が正常に動いていないようにみえる
    • make module のとき、つまりaar作成のときしか動かない?*3
    • そもそもG様が、デバック用/リリース用とaar化したモジュールを明示的にapp側で指定することを推奨している*4

あたりによるものだったりする気がするのは気のせいなのかな〜

まあ

android{
   publishNonDefault true

   defaultPublishConfig "debug"
}

とか libraryProject側のbuild.gradle にちゃんと書いて固定化制御しろ というお話は有るようですが、

AS上のlibraryProject側のBuidTypeが意味をなしていないという・・・

qiita.com

2017/03/01追記メモ

  • buildConfigのフィールドって、
    • ASだと ${XXXX} で AndroidManifest.xml で参照できる機能があり
    • <= ManifestMargerによる置換処理が働くようになるらしい

という話で、

  • GlideModuleとかClassLoaderで動的に参照する系はフルパス指定がし易い

    • activityとかだと .activity.HogeActivity とか相対指定できるけどたまに落ちることが・・
    • これもフルパッケージ指定すれば問題なかったりする*5
  • app/AndroidManifest.xml

<application>
    <meta-data
        android:name="${applicationid}.application.LimitCacheSizeGlideModule"
        android:value="GlideModule"
        />
</application>

参考

*1:なんか最近引数はContextなのに getActivity()でないとダメみたいな類似コード多いですね<汗

*2:proguardの動きとして、参照されていない? クラスは除去される<溶けてなくなってしまう?>動きになるよう

*3:library Projectを指定してgradlew assembleDebug

*4:そういう形でしかテストしていない?

*5:そもそもproguardでActivityやFragmentやContentProviderを除外設定するのは、動的にクラス名を指定して呼び出すからだよな・・その時にフルパッケージだとちゃんと探せるという話