通过意图传递枚举或对象(最佳解决方案)

jax*_*jax 204 android

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

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

我有什么选择?

我可以通过枚举吗?

旁白:有没有办法从Intent将参数传递给Activity Constructor?

pab*_*sco 527

这是一个古老的问题,但每个人都没有提到Enums实际上是Serializable因此可以完美地添加到Intent作为额外的.像这样:

public enum AwesomeEnum {
  SOMETHING, OTHER;
}

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

AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum");
Run Code Online (Sandbox Code Playgroud)

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


备择方案:

好点的是由著名tedzyc的事实,所提供的解决方案Oderik给你一个错误.然而,提供的替代方案有点崩溃 - 一些使用(甚至使用泛型).

如果您真的担心将枚举添加到意图中的表现,我建议使用这些替代方案:

选项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)];
  }
}
Run Code Online (Sandbox Code Playgroud)

用法:

// Sender usage
AwesomeEnum.SOMETHING.attachTo(intent);
// Receiver usage
AwesomeEnum result = AwesomeEnum.detachFrom(intent);
Run Code Online (Sandbox Code Playgroud)

选项2 :( 通用,可重用和与枚举分离)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

// Sender usage
EnumUtil.serialize(AwesomeEnum.Something).to(intent);
// Receiver usage
AwesomeEnum result = 
EnumUtil.deserialize(AwesomeEnum.class).from(intent);
Run Code Online (Sandbox Code Playgroud)

方案3(与Kotlin一起):

已经有一段时间了,但从现在开始我们有Kotlin,我想我会为新范例添加另一种选择.在这里,我们可以使用扩展函数和已知类型(在编译时保留类型).

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

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

这样做有一些好处.

  • 我们不需要中间对象的"开销"来进行序列化,因为它完全就位,inline因为它将用函数内的代码替换调用.
  • 这些功能更加熟悉,因为它们类似于SDK.
  • IDE将自动完成这些功能,这意味着不需要具有该实用程序类的先前知识.

用法:

// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()
Run Code Online (Sandbox Code Playgroud)

  • 用于指出枚举的+1是可序列化的. (59认同)
  • +1指出,使它们适用于应用程序是一个"真正的坏主意". (13认同)
  • 我实际上在一个项目上工作,我只是不想处理序列化或捆绑对象(很多对象中包含很多变量),并且使用静态全局变量很好......直到队友进入项目.尝试协调这些全局变量的使用的成本使我"搞砸了.我正在编写一个代码生成器来使我成为一些Parcelables".错误数量大幅下降 (3认同)
  • @Coeffect是的,这是一个可以理解的建议,但在大多数情况下,这可以被认为是过早的优化,除非你解析成千上万的枚举(在本质上它们应该只是少数,因为他们习惯于处理状态)在Nexus 4上你得到1ms的改进(http://www.developerphil.com/parcelable-vs-serializable/)不确定它是否值得额外的腿部工作,但是你又有了我建议的其他选择;) (2认同)

Ode*_*rik 114

你可以使你的enum实现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];
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用Intent.putExtra(String,Parcelable).

更新:请注意wreckgar的评论,enum.values()在每次通话时分配一个新阵列.

更新:Android Studio具有ParcelableEnum实现此解决方案的实时模板.(在Windows上,使用Ctrl+ J)

  • 也可以使用enum的toString()和valueOf()方法而不是序数. (3认同)
  • 当开发人员插入新的枚举成员时,使用ordinal()可能会中断.当然,重命名枚举成员也会破坏name().但是,开发人员更有可能插入新成员而不是重命名,因为重命名需要他重新考虑整个项目. (2认同)
  • 我不同意额外(或重新排序)枚举值比重命名值更可能.使用IntelliJ IDEA重构等复杂的IDE并不是什么大问题.但您的观点仍然很好:您必须确保序列化在任何共同实现的实现中都是一致的.对于任何类型的序列化都是如此.我认为在大多数情况下,parcelables在一个应用程序中传递,其中只有一个实现存在,所以这不应该是一个问题. (2认同)
  • values()在每次调用时都会创建一个新数组,因此最好将其缓存在例如.私有静态数组 (2认同)
  • 在一般情况下(例如数据库存储),ordinal()不安全这一事实与Android包裹无关。包裹不用于长期(持久)存储。他们死于应用程序。因此,当您添加/重命名枚举时,您将获得新的包裹。 (2认同)

Cam*_*mer 24

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

public enum CountType {
    ONE,
    TWO,
    THREE
}

private CountType count;
count = ONE;

String countString = count.name();

CountType countToo = CountType.valueOf(countString);
Run Code Online (Sandbox Code Playgroud)

在支持字符串的情况下,您应该能够毫无问题地传递枚举值.

  • 所有这些都是最简单的实现. (3认同)

小智 21

要通过意图传递枚举,您可以将枚举转换为整数.

例如:

public enum Num{A ,B}
Run Code Online (Sandbox Code Playgroud)

发送(枚举为整数):

Num send = Num.A;
intent.putExtra("TEST", send.ordinal());
Run Code Online (Sandbox Code Playgroud)

接收(整数到枚举):

Num rev;
int temp = intent.getIntExtra("TEST", -1);
if(temp >= 0 && temp < Num.values().length)
    rev = Num.values()[temp];
Run Code Online (Sandbox Code Playgroud)

最好的祝福.:)

  • 或者您可以使用Num.A.name()将其作为字符串发送(因此它是可读的),然后使用Num.ValueOf(intent.getStringExtra("TEST"))将其恢复 (8认同)

Jan*_*kel 15

如果你真的需要,可以使用name()和序列化enum作为String,valueOf(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;
     }
   }
 }
Run Code Online (Sandbox Code Playgroud)

如果你的枚举有可变状态(他们不应该,真的),这显然不起作用.