Androidにおけるenum利用に関するGoogleの公式見解と考察


Androidにおけるenum利用について度々話題に登る今日このごろですが、そんな中GoogleがYoutubeでenumのパフォーマンスに関する動画を公開しました。動画は[こちら](https://www.youtube.com/watch?v=Hzs6OBcvNQE&index=86&list=PLOU2XLYxmsIJDPXCTt5TLDu67271PruEk&linkId=16543131) です。

Androidにおけるenumのパフォーマンス問題についてあまり詳しくなかったので、動画を観てみました。

##Googleはenumの何がパフォーマンスとして問題としているのか
Android OSはアプリケーションを実行する時に、Application Heap領域を確保します。このメモリ内にアプリの実行に関わるdex(Dalvik Executable) fileがロードされ、適宜実行されるわけです。

例えば2556bytesのdex fileがロードされるソースコードがあるとします。
これに対し3つのstaticなintを生成すると、2680bytes(+124bytes)に増加します。

```java

public static final int VALUE1 = 1;
public static final int VALUE2 = 2;
public static final int VALUE3 = 3;

```
一方、これをenumで表現すると、4188bytes(+1632bytes)となり、intに比べてdexサイズがが13倍多くなります。

```java
public static enum Value {
  VALUE1,
  VALUE2,
  VALUE3
}
```


少しのenumだと大した問題じゃないけど、大量に利用していると、気づけばメモリがいっぱいいっぱいになって手に負えなくなるよ、ということで、動画では[グレムリン](https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AC%E3%83%A0%E3%83%AA%E3%83%B3_(%E6%98%A0%E7%94%BB))に例えられてます。

##実際のところどれだけ影響あるのか
ここで、Googleが述べるパフォーマンスの問題は現実的にはどれくらい影響があるのか考えてみました。
あるアプリが20画面(=Activity)あるとします。それぞれFragmentを1つ持つとし、また20機能がありそれぞれに対してHelper, AsyncTask,モデルが1つずつあると仮定します。
そうすると大体 20画面 * 2 +  20機能 * 3 = 100クラスあると設定できます。
１クラスに対して3つの変数があるenumが2つあるとします。
すると、このアプリを起動した時にメモリの増加量は

1632bytes * 2 * 100 = 326400 ≒ 318kb

と試算できます。

ざっくり仮説ベースでの試算ではありますが、例え5倍になったとしても、1.5MB程度なので、最近のアプリであれば大した影響はないんじゃないかなぁと思いました。(なんか思ったよりもえらく少ないので、計算間違えてたら、ご指摘ください)


##Googleの示すenumとのつきあいかた

という個人的な見解はさておき、Googleの動画では、enumを利用することにより得られる恩恵も受けながら、パフォーマンスを前向きにあげてこ！ということで以下の対策を提示しています。

###`@IntDef` annotationを利用する
Support Annotationによって提供される `@IntDef`を利用することで、列挙型のメリットである独自の型をメソッドに定義することができます。
例えば以下のような場合 `setNavigationMode`にはint型だとなんでも入ってしまいます。


```java
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`に独自の型を定義できます。

```java
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);
```
とすることで、

```java
setNavigationMode(NAVIGATION_MODE_STANDARD); //OK
setNavigationMode(0); //NG
```

というように、変数名のみ代入できるようになります。安全ですね。詳細は以下にあります。
https://developer.android.com/reference/android/support/annotation/IntDef.html

###Proguardを設定する
こっちはすでにenumを使っている場合の対応です。proguard を設定することで、enumのメモリ利用を最適化することができるそうです。(詳細は調べてません、すいません)

##一方Jake先生は

<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">If you use integer constants instead of enums in your Android app you are a fool. <a href="https://twitter.com/mttkay">@mttkay</a>&#39;s reply here is spot-on: <a href="https://t.co/ByedAuLTi1">https://t.co/ByedAuLTi1</a></p>&mdash; Jake Wharton (@JakeWharton) <a href="https://twitter.com/JakeWharton/status/551876948469620737">January 4, 2015</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

> Androidでint定数使って enum使ってない奴馬鹿じゃね？


と割と物騒な発言をされていますが、リンク先の[Matthias Käppler氏のコメント](https://plus.google.com/105051985738280261832/posts/YDykw2hstUu)をたどると、

* 我々は組み込みエンジニアではなく、Webエンジニアである。与えられたツールで安定したアプリを書けないといけない。enumが適切でないというならそもそもJavaというツールをAndroidプラットフォームで使われることが間違っている。
* Googleから提供されるパフォーマンスに関するアドバイスはたいてい瑣末な問題だ。enumやforループなんかよりも、レイアウト描画の方がよっぽどAndroidのパフォーマンスに影響する。

などなど至極まっとうなことを書かれています。

#結論
今回enumについて調べてみたのですが、大げさに考えるのは今の時代あんまり意味ないんじゃないかな、と(恐らく多くの人が思っているであろう)結論にいたりました。