我有一个活动,启动时需要访问两个不同的数组列表。两个列表都是我自己创建的不同对象。

基本上,我需要一种方法来将这些对象从Intent传递给活动。我可以使用addExtras(),但这需要一个Parceable兼容类。我可以使我的类传递序列化,但据我所知,这减慢了程序。

我有什么选择?

我可以传递一个Enum吗?

题外话:有没有一种方法可以将参数从Intent传递给Activity构造函数?


当前回答

您可以将枚举作为字符串传递。

public enum CountType {
    ONE,
    TWO,
    THREE
}

private CountType count;
count = ONE;

String countString = count.name();

CountType countToo = CountType.valueOf(countString);

给定字符串的支持,你应该能够传递枚举的值没有问题。

其他回答

你可以让你的枚举实现Parcelable,这对枚举来说非常简单:

public enum MyEnum implements Parcelable {
    VALUE;


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

    @Override
    public void writeToParcel(final Parcel dest, final int flags) {
        dest.writeInt(ordinal());
    }

    public static final Creator<MyEnum> CREATOR = new Creator<MyEnum>() {
        @Override
        public MyEnum createFromParcel(final Parcel source) {
            return MyEnum.values()[source.readInt()];
        }

        @Override
        public MyEnum[] newArray(final int size) {
            return new MyEnum[size];
        }
    };
}

然后你可以使用Intent。Parcelable putExtra(字符串)。

更新:请注意沉船的评论,枚举.values()分配一个新的数组在每次调用。

更新:Android Studio提供了一个实时模板ParcelableEnum来实现这个解决方案。(在Windows上,使用Ctrl+J)

如果你只是想发送一个枚举,你可以这样做:

首先声明一个包含一些值的枚举(可以通过intent传递):

 public enum MyEnum {
    ENUM_ZERO(0),
    ENUM_ONE(1),
    ENUM_TWO(2),
    ENUM_THREE(3);
    private int intValue;

    MyEnum(int intValue) {
        this.intValue = intValue;
    }

    public int getIntValue() {
        return intValue;
    }

    public static MyEnum getEnumByValue(int intValue) {
        switch (intValue) {
            case 0:
                return ENUM_ZERO;
            case 1:
                return ENUM_ONE;
            case 2:
                return ENUM_TWO;
            case 3:
                return ENUM_THREE;
            default:
                return null;
        }
    }
}

然后:

  intent.putExtra("EnumValue", MyEnum.ENUM_THREE.getIntValue());

当你想要得到它时:

  NotificationController.MyEnum myEnum = NotificationController.MyEnum.getEnumByValue(intent.getIntExtra("EnumValue",-1);

小菜一碟!

它可以使你的Enum实现Serializable,然后你可以通过Intent传递它,因为有一个方法可以将它作为一个Serializable传递。使用int而不是enum的建议是错误的。枚举用于使代码更易于阅读和维护。如果不能使用枚举,这将是倒退到黑暗时代的一大步。

这是一个老问题,但每个人都没有提到枚举实际上是可序列化的,因此可以完美地添加到一个额外的意图。是这样的:

public enum AwesomeEnum {
  SOMETHING, OTHER;
}

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

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

建议使用静态变量或应用程序范围的变量是一个非常糟糕的主意。这实际上是将您的活动与状态管理系统结合在一起,并且很难维护、调试和解决问题。


选择:

tedzyc提到了一个很好的观点,即Oderik提供的解决方案会给您一个错误。但是,提供的替代方法使用起来有点麻烦(即使使用泛型)。

如果你真的担心将枚举添加到Intent的性能,我建议以下替代方案:

选项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);

选项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);

选项3(与Kotlin):

已经有一段时间了,但既然现在我们有了Kotlin,我想我应该为新范例添加另一个选项。在这里,我们可以使用扩展函数和具体化类型(在编译时保留类型)。

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] }

这样做有几个好处。

我们不需要中间对象的“开销”来执行序列化,因为内联将用函数内部的代码替换调用,所有这些都已经完成。 这些功能更加熟悉,因为它们与SDK的功能相似。 IDE将自动完成这些功能,这意味着您不需要事先了解实用工具类。

其中一个缺点是,如果我们改变Emums的顺序,那么任何旧的参考都将不起作用。这可能是一个问题,比如挂起意图中的intent,因为它们可能在更新后仍然存在。然而,对于其余的时间,它应该是可以的。

需要注意的是,如果重命名任何值,其他解决方案(如使用名称而不是位置)也会失败。不过,在这些情况下,我们会得到一个异常,而不是错误的Enum值。

用法:

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

如果你真的需要,你可以使用name()和valueOf(String)将一个enum序列化为String,如下所示:

 class Example implements Parcelable { 
   public enum Foo { BAR, BAZ }

   public Foo fooValue;

   public void writeToParcel(Parcel dest, int flags) {
      parcel.writeString(fooValue == null ? null : fooValue.name());
   }

   public static final Creator<Example> CREATOR = new Creator<Example>() {
     public Example createFromParcel(Parcel source) {        
       Example e = new Example();
       String s = source.readString(); 
       if (s != null) e.fooValue = Foo.valueOf(s);
       return e;
     }
   }
 }

如果你的枚举有可变的状态,这显然是行不通的(他们不应该,真的)。