Androidにおけるenum利用について度々話題に登る今日このごろですが、そんな中GoogleがYoutubeでenumのパフォーマンスに関する動画を公開しました。動画はこちら です。
Androidにおけるenumのパフォーマンス問題についてあまり詳しくなかったので、動画を観てみました。
##Googleはenumの何がパフォーマンスとして問題としているのか
Android OSはアプリケーションを実行する時に、Application Heap領域を確保します。このメモリ内にアプリの実行に関わるdex(Dalvik Executable) fileがロードされ、適宜実行されるわけです。
例えば2556bytesのdex fileがロードされるソースコードがあるとします。
これに対し3つのstaticなintを生成すると、2680bytes(+124bytes)に増加します。
public static final int VALUE1 = 1;
public static final int VALUE2 = 2;
public static final int VALUE3 = 3;
一方、これをenumで表現すると、4188bytes(+1632bytes)となり、intに比べてdexサイズがが13倍多くなります。
public static enum Value {
VALUE1,
VALUE2,
VALUE3
}
少しのenumだと大した問題じゃないけど、大量に利用していると、気づけばメモリがいっぱいいっぱいになって手に負えなくなるよ、ということで、動画ではグレムリンに例えられてます。
##実際のところどれだけ影響あるのか
ここで、Googleが述べるパフォーマンスの問題は現実的にはどれくらい影響があるのか考えてみました。
あるアプリが20画面(=Activity)あるとします。それぞれFragmentを1つ持つとし、また20機能がありそれぞれに対してHelper, AsyncTask,モデルが1つずつあると仮定します。
そうすると大体 20画面 * 2 + 20機能 * 3 = 100クラスあると設定できます。
1クラスに対して3つの変数があるenumが2つあるとします。
すると、このアプリを起動した時にメモリの増加量は
1632bytes * 2 * 100 = 326400 ≒ 318kb
と試算できます。
ざっくり仮説ベースでの試算ではありますが、例え5倍になったとしても、1.5MB程度なので、最近のアプリであれば大した影響はないんじゃないかなぁと思いました。(なんか思ったよりもえらく少ないので、計算間違えてたら、ご指摘ください)
##Googleの示すenumとのつきあいかた
という個人的な見解はさておき、Googleの動画では、enumを利用することにより得られる恩恵も受けながら、パフォーマンスを前向きにあげてこ!ということで以下の対策を提示しています。
###@IntDef
annotationを利用する
Support Annotationによって提供される @IntDef
を利用することで、列挙型のメリットである独自の型をメソッドに定義することができます。
例えば以下のような場合 setNavigationMode
にはint型だとなんでも入ってしまいます。
public abstract class ActionBar {
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
public abstract void setNavigationMode(int mode);
}
Support Annotationを使うとsetNavigationMode
に独自の型を定義できます。
import android.support.annotation.IntDef;
...
public abstract class ActionBar {
...
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {} //独自の型
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
@NavigationMode
public abstract int getNavigationMode();
public abstract void setNavigationMode(@NavigationMode int mode);
とすることで、
setNavigationMode(NAVIGATION_MODE_STANDARD); //OK
setNavigationMode(0); //NG
というように、変数名のみ代入できるようになります。安全ですね。詳細は以下にあります。
https://developer.android.com/reference/android/support/annotation/IntDef.html
###Proguardを設定する
こっちはすでにenumを使っている場合の対応です。proguard を設定することで、enumのメモリ利用を最適化することができるそうです。(詳細は調べてません、すいません)
##一方Jake先生は
If you use integer constants instead of enums in your Android app you are a fool. @mttkay's reply here is spot-on: https://t.co/ByedAuLTi1
— Jake Wharton (@JakeWharton) January 4, 2015
Androidでint定数使って enum使ってない奴馬鹿じゃね?
と割と物騒な発言をされていますが、リンク先のMatthias Käppler氏のコメントをたどると、
- 我々は組み込みエンジニアではなく、Webエンジニアである。与えられたツールで安定したアプリを書けないといけない。enumが適切でないというならそもそもJavaというツールをAndroidプラットフォームで使われることが間違っている。
- Googleから提供されるパフォーマンスに関するアドバイスはたいてい瑣末な問題だ。enumやforループなんかよりも、レイアウト描画の方がよっぽどAndroidのパフォーマンスに影響する。
などなど至極まっとうなことを書かれています。
#結論
今回enumについて調べてみたのですが、大げさに考えるのは今の時代あんまり意味ないんじゃないかな、と(恐らく多くの人が思っているであろう)結論にいたりました。