1. ホーム
  2. アンドロイド

[解決済み】インテントでenumやオブジェクトを渡す(最適な解決方法)。

2022-04-03 19:35:04

質問

開始時に2つの異なるArrayListにアクセスする必要があるアクティビティがあります。 両方のリストは、私自身が作成した異なるオブジェクトです。

基本的に、私はIntentからアクティビティにこれらのオブジェクトを渡す方法が必要です。 私はaddExtras()を使用することができますが、これはParceableと互換性のあるクラスを必要とします。 私はシリアライザブルに渡されるように私のクラスを作ることができましたが、私が理解するように、これはプログラムを遅くします。

どのような選択肢がありますか?

Enumを渡すことはできますか?

余談ですが、インテントからアクティビティ コンストラクタにパラメータを渡す方法はありますか?

どのように解決するのですか?

これは古い質問ですが、誰もがEnumsが実際には Serializable そのため、Intentに追加することができます。このように。

public enum AwesomeEnum {
  SOMETHING, OTHER;
}

intent.putExtra("AwesomeEnum", AwesomeEnum.SOMETHING);

AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum");

静的変数やアプリケーション全体の変数を使うという提案は、本当に悪い考えです。これは本当にあなたの活動を状態管理システムに結合してしまい、メンテナンス、デバッグ、問題解決を困難にします。


ALTERNATIVES

良い点を指摘されたのは テジーク が提供するソリューションが オーデリック がエラーになります。しかし、提示された代替案は、(ジェネリックを使っても)ちょっと使いづらいです。

もし、Intentにenumを追加する際のパフォーマンスについて本当に心配しているのであれば、代わりに以下の代替案を提案します。

オプション1:

public enum AwesomeEnum {
  SOMETHING, OTHER;
  private static final String name = AwesomeEnum.class.getName();
  public void attachTo(Intent intent) {
    intent.putExtra(name, ordinal());
  }
  public static AwesomeEnum detachFrom(Intent intent) {
    if(!intent.hasExtra(name)) throw new IllegalStateException();
    return values()[intent.getIntExtra(name, -1)];
  }
}

使用方法

// Sender usage
AwesomeEnum.SOMETHING.attachTo(intent);
// Receiver usage
AwesomeEnum result = AwesomeEnum.detachFrom(intent);

OPTION 2: (汎用的、再利用可能、enumから切り離された)

public final class EnumUtil {
    public static class Serializer<T extends Enum<T>> extends Deserializer<T> {
        private T victim;
        @SuppressWarnings("unchecked") 
        public Serializer(T victim) {
            super((Class<T>) victim.getClass());
            this.victim = victim;
        }
        public void to(Intent intent) {
            intent.putExtra(name, victim.ordinal());
        }
    }
    public static class Deserializer<T extends Enum<T>> {
        protected Class<T> victimType;
        protected String name;
        public Deserializer(Class<T> victimType) {
            this.victimType = victimType;
            this.name = victimType.getName();
        }
        public T from(Intent intent) {
            if (!intent.hasExtra(name)) throw new IllegalStateException();
            return victimType.getEnumConstants()[intent.getIntExtra(name, -1)];
        }
    }
    public static <T extends Enum<T>> Deserializer<T> deserialize(Class<T> victim) {
        return new Deserializer<T>(victim);
    }
    public static <T extends Enum<T>> Serializer<T> serialize(T victim) {
        return new Serializer<T>(victim);
    }
}

使用方法

// Sender usage
EnumUtil.serialize(AwesomeEnum.Something).to(intent);
// Receiver usage
AwesomeEnum result = 
EnumUtil.deserialize(AwesomeEnum.class).from(intent);

OPTION 3 (Kotlinを使用)。

少し時間が経ってしまいましたが、Kotlinが登場したので、新しいパラダイムに対応した別のオプションを追加しておこうと思います。ここでは、拡張関数とreified型(コンパイル時に型が保持される)を利用することができる。

inline fun <reified T : Enum<T>> Intent.putExtra(victim: T): Intent =
    putExtra(T::class.java.name, victim.ordinal)

inline fun <reified T: Enum<T>> Intent.getEnumExtra(): T? =
    getIntExtra(T::class.java.name, -1)
        .takeUnless { it == -1 }
        ?.let { T::class.java.enumConstants[it] }

このようにすると、いくつかの利点があります。

  • のおかげで、シリアライズを行うための中間オブジェクトのオーバーヘッドを必要としない。 inline で、その呼び出しを関数内のコードに置き換えます。
  • 関数はSDKのものと似ているため、より親しみやすいと思います。
  • IDEがこれらの関数をオートコンプリートするので、ユーティリティクラスに関する予備知識は必要ありません。

デメリットとしては、Emumsの順番を変えると、古い参照は使えなくなることです。これは、保留中のIntentの内部で、更新に耐えられない可能性があるため、問題になることがあります。しかし、それ以外の場合は、問題ないでしょう。

注意すべきは、位置の代わりに名前を使うような他の解決策も、いずれかの値の名前を変更すると失敗することです。ただし、そのような場合は、不正なEnum値の代わりに例外が発生します。

使用方法

// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()