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

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

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

我有什么选择?

我可以传递一个Enum吗?

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

122227 次浏览

我认为最好的办法是将这些列表转换为一些可打包的东西,如字符串(或映射?),以将其传递给活动。然后Activity将不得不将其转换回数组。

实现自定义包装是一个痛苦的脖子,所以我将尽可能避免它。

不要使用枚举。不使用枚举的第78个理由。:)使用整数,可以通过Bundle和Parcelable轻松远程。

如果你真的需要,你可以使用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;
}
}
}

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

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

要按意图传递枚举,可以将enum转换为整数。

例:

public enum Num{A ,B}

发送(enum to integer):

Num send = Num.A;
intent.putExtra("TEST", send.ordinal());

接收(整数到enum):

Num rev;
int temp = intent.getIntExtra("TEST", -1);
if(temp >= 0 && temp < Num.values().length)
rev = Num.values()[temp];
< p >致以最亲切的问候。 :) < / p >

你可以让你的枚举实现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(字符串)。

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

更新:Android Studio功能的活模板ParcelableEnum实现这个解决方案。(在Windows上,使用Ctrl+J)

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

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

< em >选项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] }

这样做有几个好处。

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

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

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

用法:

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

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

public enum CountType {
ONE,
TWO,
THREE
}


private CountType count;
count = ONE;


String countString = count.name();


CountType countToo = CountType.valueOf(countString);

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

关于Oderik的帖子:

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

public enum MyEnum实现Parcelable { ... } 你可以使用Intent。putExtra(字符串,Parcelable)。< / p >

如果你定义了MyEnum变量MyEnum,那么执行intent.putExtra("Parcelable1", MyEnum),你将得到一个"方法putExtra(String, Parcelable)对于类型Intent"错误消息。 因为还有一个意图。putExtra(String, Parcelable)方法,和原来的'Enum'类型本身实现了Serializable接口,所以编译器不知道选择哪个方法(意图。putExtra(String, Parcelable/or Serializable)).

建议从MyEnum中删除Parcelable接口,并将核心代码移动到wrap类的Parcelable实现中,就像这样(Father2是一个Parcelable,包含一个enum字段):

public class Father2 implements Parcelable {


AnotherEnum mAnotherEnum;
int mField;


public Father2(AnotherEnum myEnum, int field) {
mAnotherEnum = myEnum;
mField = field;
}


private Father2(Parcel in) {
mField = in.readInt();
mAnotherEnum = AnotherEnum.values()[in.readInt()];
}


public static final Parcelable.Creator<Father2> CREATOR = new Parcelable.Creator<Father2>() {


public Father2 createFromParcel(Parcel in) {
return new Father2(in);
}


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


};


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


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


}

然后我们可以做:

AnotherEnum anotherEnum = AnotherEnum.Z;
intent.putExtra("Serializable2", AnotherEnum.X);
intent.putExtra("Parcelable2", new Father2(AnotherEnum.X, 7));

考虑以下enum::

public static  enum MyEnum {
ValueA,
ValueB
}

对于通过::

 Intent mainIntent = new Intent(this,MyActivity.class);
mainIntent.putExtra("ENUM_CONST", MyEnum.ValueA);
this.startActivity(mainIntent);

从intent/bundle/arguments中返回:

 MyEnum myEnum = (MyEnum) intent.getSerializableExtra("ENUM_CONST");

我喜欢简单。

  • Fred活动有两种模式——HAPPYSAD
  • 创建一个静态的IntentFactory,为你创建你的Intent。传递给它你想要的Mode
  • IntentFactory使用Mode类的名称作为额外对象的名称。
  • IntentFactory使用name()Mode转换为String
  • 在进入onCreate时,使用此信息转换回Mode
  • 你也可以使用ordinal()Mode.values()。我喜欢字符串,因为我可以在调试器中看到它们。

    public class Fred extends Activity {
    
    
    public static enum Mode {
    HAPPY,
    SAD,
    ;
    }
    
    
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.betting);
    Intent intent = getIntent();
    Mode mode = Mode.valueOf(getIntent().getStringExtra(Mode.class.getName()));
    Toast.makeText(this, "mode="+mode.toString(), Toast.LENGTH_LONG).show();
    }
    
    
    public static Intent IntentFactory(Context context, Mode mode){
    Intent intent = new Intent();
    intent.setClass(context,Fred.class);
    intent.putExtra(Mode.class.getName(),mode.name());
    
    
    return intent;
    }
    }
    

你可以使用枚举构造函数为enum设置基本数据类型。

public enum DaysOfWeek {
MONDAY(1),
TUESDAY(2),
WEDNESDAY(3),
THURSDAY(4),
FRIDAY(5),
SATURDAY(6),
SUNDAY(7);


private int value;
private DaysOfWeek(int value) {
this.value = value;
}


public int getValue() {
return this.value;
}


private static final SparseArray<DaysOfWeek> map = new SparseArray<DaysOfWeek>();


static
{
for (DaysOfWeek daysOfWeek : DaysOfWeek.values())
map.put(daysOfWeek.value, daysOfWeek);
}


public static DaysOfWeek from(int value) {
return map.get(value);
}
}

你可以使用传递int作为额外参数,然后使用它的值从enum中取出它。

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

首先声明一个包含一些值的枚举(可以通过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);

小菜一碟!

使用Kotlin扩展函数

inline fun <reified T : Enum<T>> Intent.putExtra(enumVal: T, key: String? = T::class.qualifiedName): Intent =
putExtra(key, enumVal.ordinal)


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

这使您可以灵活地传递多个相同的枚举类型,或者默认使用类名。

// Add to gradle
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"


// Import the extension functions
import path.to.my.kotlin.script.putExtra
import path.to.my.kotlin.script.getEnumExtra


// To Send
intent.putExtra(MyEnumClass.VALUE)


// To Receive
val result = intent.getEnumExtra<MyEnumClass>()

大多数使用Parcelable概念的答案都是在Java代码中。在Kotlin中更容易做到这一点。

只需用@Parcelize注释枚举类并实现Parcelable接口。

@Parcelize
enum class ViewTypes : Parcelable {
TITLE, PRICES, COLORS, SIZES
}