2011年5月14日土曜日

enum列挙子をプロセス間通信でやり取りする方法

enumがParcelableならそのまま渡せるんだけど、調べても大して見つからない。 カスタムクラスにenum定数を持たせる方法とかはあったんだけど、 AIDLインタフェースの引数に直接enum定数をやり取りする情報は見つからない。 そんなに需要がないんだろうか。。。

たとえば、こんなenum型があったとして、

public enum EnumTest {
  ENUM_1, ENUM_2, ENUM_3;
}

こんな感じでAIDLのインタフェースとやり取りしたい場合があるんだけど、

package sample.test;

import sample.test.EnumTest;

interface TestService {
  void test(in EnumTest e);    // エラー
}

これは当然EnumTestがParcelableを実装していないのでエラーになる。

でも、enumも実際は特殊なクラスなんだから、おそらくParcelableを実装したらいいんだろうなと予測はできる。 というわけで、やってみた。

まずは、Parcelableを実装したenumを作成する。

public enum ParcelableEnumTest implements Parcelable {
  ENUM_1, ENUM_2, ENUM_3;

  @Override
  public int describeContents() {
    return 0;
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    // StringでParcelに書き出す
    dest.writeString(this.name());
  }

  public static final Parcelable.Creator<ParcelableEnumTest> CREATOR = 
      new Parcelable.Creator<ParcelableEnumTest>() {
        @Override
        public ParcelableEnumTest[] newArray(int size) {
          return new ParcelableEnumTest[size];
        }

        @Override
        public ParcelableEnumTest createFromParcel(Parcel source) {
          // StringでParcelから読み出してenumに変換する
          return ParcelableEnumTest.valueOf(source.readString());
        }
      };
}

次に、同パッケージにAIDLファイルを作成。

package sample.test;

parcelable ParcelableEnumTest;

そんでもって、AIDLインタフェースで引数にParcelableを実装したenumを指定する。

package sample.test;

import sample.test.ParcelableEnumTest;

interface TestService {
  void test(in ParcelableEnumTest e);
}

するとまぁ一応Serviceをバインドしてenumをやり取りできたんだが、こんなんであってんのかね?

Parcelへの書き込みと読み出しには、一旦Stringを経由して行っているので、読み出しはちょっと効率が悪いかもしれない。 enumって確かSerializableだから、Parcel#writeSerializable()Parcel#readSerializable()でもいけるかもと思ったんだけど、ダメだった。

そもそもenumじゃなくて、intのクラス定数(static final int)を使った方がパフォーマンス的に有利だし、面倒が少なそう。 enumにimplementsを書くのも気持ち悪いし、公式ドキュメントにもパフォーマンスを意識するならenum使うなって書いてあった気がするしね*1。 実用的ではないんだろう。

だから、enum列挙子でやり取りしたいっていう需要がないのかなぁ。

参考

Android Interface Definition Language (AIDL) | Android Developers
Designing for Performance | Android Developers
Android - Avoid Enums


^1:と思ったら、いつの間にかDesigning for PerformanceからAvoid Enumsの項目が消えてた。 どういうこと?VMが改良されて今では気にしなくても良くなったりしたのかな? 昔書いてあった内容はLinuxtopiaに残ってるっぽい。

1 件のコメント:

  1. 私も enum が Parcelable だったらいいのにと思っていてい調べていたらここにたどり着きました。
    大変参考になりました。

    enum EnumTest と宣言をすると Enum を継承したクラスになるようで、
    このジェネリッククラス Enum<> に定義されている ordinal() というメソッドが enum の定義のインデックスを返すので、
    これを利用すると文字列を用いる場合にくらべて高速になるかと思います。

    返信削除