Android 通过意图传递枚举或对象(最佳解决方案)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2836256/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Passing enum or object through an intent (the best solution)
提问by jax
I have an activity that when started needs access to two different ArrayLists. Both Lists are different Objects I have created myself.
我有一个活动,在开始时需要访问两个不同的 ArrayList。两个列表都是我自己创建的不同对象。
Basically I need a way to pass these objects to the activity from an Intent. I can use addExtras() but this requires a Parceable compatible class. I could make my classes to be passed serializable but as I understand this slows down the program.
基本上,我需要一种方法将这些对象从 Intent 传递给 Activity。我可以使用 addExtras() 但这需要 Parceable 兼容类。我可以让我的类通过可序列化但据我所知这会减慢程序的速度。
What are my options?
我有哪些选择?
Can I pass an Enum?
我可以通过枚举吗?
As an an aside: is there a way to pass parameters to an Activity Constructor from an Intent?
顺便说一句:有没有办法将参数从意图传递给活动构造函数?
回答by pablisco
This is an old question, but everybody fails to mention that Enums are actually Serializable
and therefore can perfectly be added to an Intent as an extra. Like this:
这是一个老问题,但每个人都没有提到枚举实际上是Serializable
,因此可以作为额外的完美添加到意图中。像这样:
public enum AwesomeEnum {
SOMETHING, OTHER;
}
intent.putExtra("AwesomeEnum", AwesomeEnum.SOMETHING);
AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum");
The suggestion to use static or application-wide variables is a really bad idea. This really couples your activities to a state managing system, and it is hard to maintain, debug and problem bound.
建议使用静态或应用程序范围的变量是一个非常糟糕的主意。这确实将您的活动与状态管理系统耦合在一起,并且难以维护、调试和问题绑定。
ALTERNATIVES:
备择方案:
A good point was noted by tedzycabout the fact that the solution provided by Oderikgives you an error. However, the alternative offered is a bit cumbersome to use (even using generics).
tedzyc指出了一个很好的观点,即Oderik提供的解决方案给您一个错误。但是,提供的替代方案使用起来有点麻烦(即使使用泛型)。
If you are really worried about the performance of adding the enum to an Intent I propose these alternatives instead:
如果您真的担心将枚举添加到 Intent 的性能,我建议使用以下替代方案:
OPTION 1:
选项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)];
}
}
Usage:
用法:
// Sender usage
AwesomeEnum.SOMETHING.attachTo(intent);
// Receiver usage
AwesomeEnum result = AwesomeEnum.detachFrom(intent);
OPTION 2:(generic, reusable and decoupled from the enum)
选项 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);
}
}
Usage:
用法:
// Sender usage
EnumUtil.serialize(AwesomeEnum.Something).to(intent);
// Receiver usage
AwesomeEnum result =
EnumUtil.deserialize(AwesomeEnum.class).from(intent);
OPTION 3 (with Kotlin):
选项 3(使用 Kotlin):
It's been a while, but since now we have Kotlin, I thought I would add another option for the new paradigm. Here we can make use of extension functions and reified types (which retains the type when compiling).
已经有一段时间了,但既然现在我们有了 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] }
There are a few benefits of doing it this way.
这样做有几个好处。
- We don't require the "overhead" of an intermediary object to do the serialization as it's all done in place thanks to
inline
which will replace the calls with the code inside the function. - The functions are more familiar as they are similar to the SDK ones.
- The IDE will autocomplete these functions which means there is no need to have previous knowledge of the utility class.
- 我们不需要中间对象的“开销”来进行序列化,因为这一切都已就位完成,
inline
这将用函数内部的代码替换调用。 - 功能更熟悉,因为它们类似于 SDK 的功能。
- IDE 将自动完成这些功能,这意味着无需事先了解实用程序类。
One of the downsides is that, if we change the order of the Emums, then any old reference will not work. This can be an issue with things like Intents inside pending intents as they may survive updates. However, for the rest of the time, it should be ok.
缺点之一是,如果我们更改 Emum 的顺序,则任何旧参考都将不起作用。这可能是待处理意图中的诸如 Intent 之类的问题,因为它们可能会在更新后继续存在。但是,其余时间应该没问题。
It's important to note that other solutions, like using the name instead of the position, will also fail if we rename any of the values. Although, in those cases, we get an exception instead of the incorrect Enum value.
重要的是要注意,如果我们重命名任何值,其他解决方案(例如使用名称而不是位置)也会失败。尽管在这些情况下,我们会得到一个异常而不是错误的 Enum 值。
Usage:
用法:
// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()
回答by Oderik
You can make your enum implement Parcelable which is quite easy for enums:
你可以让你的枚举实现 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];
}
};
}
You can then use Intent.putExtra(String, Parcelable).
然后您可以使用 Intent.putExtra(String, Parcelable)。
UPDATE: Please note wreckgar's comment that enum.values()
allocates a new array at each call.
更新:请注意 wreckgar 的评论,即enum.values()
在每次调用时分配一个新数组。
UPDATE: Android Studio features a live template ParcelableEnum
that implements this solution. (On Windows, use Ctrl+J)
更新:Android Studio 提供了一个ParcelableEnum
实现此解决方案的实时模板。(在 Windows 上,使用Ctrl+ J)
回答by Cameron Lowell Palmer
You can pass an enum through as a string.
您可以将枚举作为字符串传递。
public enum CountType {
ONE,
TWO,
THREE
}
private CountType count;
count = ONE;
String countString = count.name();
CountType countToo = CountType.valueOf(countString);
Given strings are supported you should be able to pass the value of the enum around with no problem.
鉴于支持字符串,您应该能够毫无问题地传递枚举的值。
回答by TURTLE
For passing an enum by intent, you can convert enum into integer.
为了通过意图传递枚举,您可以将枚举转换为整数。
Ex:
前任:
public enum Num{A ,B}
Sending(enum to integer):
发送(枚举到整数):
Num send = Num.A;
intent.putExtra("TEST", send.ordinal());
Receiving(integer to enum):
接收(整数到枚举):
Num rev;
int temp = intent.getIntExtra("TEST", -1);
if(temp >= 0 && temp < Num.values().length)
rev = Num.values()[temp];
Best regards. :)
此致。:)
回答by Jan Berkel
If you really need to, you could serialize an enum as a String, using name()
and valueOf(String)
, as follows:
如果确实需要,可以使用name()
and将枚举序列化为字符串,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;
}
}
}
This obviously doesn't work if your enums have mutable state (which they shouldn't, really).
如果您的枚举具有可变状态(实际上它们不应该如此),这显然不起作用。
回答by Damien Cooke
It may be possible to make your Enum implement Serializable then you can pass it via the Intent, as there is a method for passing it as a serializable. The advice to use int instead of enum is bogus. Enums are used to make your code easier to read and easier to maintain. It would a large step backwards into the dark ages to not be able to use Enums.
有可能让你的 Enum 实现 Serializable 然后你可以通过 Intent 传递它,因为有一种方法可以将它作为可序列化传递。使用 int 而不是 enum 的建议是虚假的。枚举用于使您的代码更易于阅读和维护。如果不能使用枚举,那将向黑暗时代倒退一大步。
回答by tedzyc
about Oderik's post:
关于 Oderik 的帖子:
You can make your enum implement Parcelable which is quite easy for enums:
public enum MyEnum implements Parcelable { ... } You can than use Intent.putExtra(String, Parcelable).
你可以让你的枚举实现 Parcelable 这对于枚举来说很容易:
public enum MyEnum 实现 Parcelable { ... } 你可以使用 Intent.putExtra(String, Parcelable)。
If you define a MyEnum variable myEnum, then do intent.putExtra("Parcelable1", myEnum), you will get a "The method putExtra(String, Parcelable) is ambiguous for the type Intent" error message. because there is also a Intent.putExtra(String, Parcelable) method, and original 'Enum' type itself implements the Serializable interface, so compiler does not know choose which method(intent.putExtra(String, Parcelable/or Serializable)).
如果你定义了一个 MyEnum 变量 myEnum,然后执行 intent.putExtra("Parcelable1", myEnum),你会得到一个“The method putExtra(String, Parcelable) is ambiguous for the type Intent”的错误信息。因为还有一个 Intent.putExtra(String, Parcelable) 方法,而原来的 'Enum' 类型本身就实现了 Serializable 接口,所以编译器不知道选择哪个方法(intent.putExtra(String, Parcelable/or Serializable))。
Suggest that remove the Parcelable interface from MyEnum, and move the core code into wrap class' Parcelable implementation, like this(Father2 is a Parcelable and contain an enum field):
建议去掉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());
}
}
then we can do:
那么我们可以这样做:
AnotherEnum anotherEnum = AnotherEnum.Z;
intent.putExtra("Serializable2", AnotherEnum.X);
intent.putExtra("Parcelable2", new Father2(AnotherEnum.X, 7));
回答by niczm25
you can use enum constructor for enum to have primitive data type..
您可以使用 enum 构造函数使 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);
}
}
you can use to pass int as extras then pull it from enum using its value.
您可以使用 int 作为 extras 传递,然后使用它的值从 enum 中提取它。
回答by JohnnyLambada
I like simple.
我喜欢简单。
- The Fred activity has two modes --
HAPPY
andSAD
. - Create a static
IntentFactory
that creates yourIntent
for you. Pass it theMode
you want. - The
IntentFactory
uses the name of theMode
class as the name of the extra. - The
IntentFactory
converts theMode
to aString
usingname()
- Upon entry into
onCreate
use this info to convert back to aMode
. You could use
ordinal()
andMode.values()
as well. I like strings because I can see them in the debugger.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; } }
- Fred 活动有两种模式 -
HAPPY
和SAD
. - 创建一个静态
IntentFactory
,Intent
为您创建您的。传递它Mode
你想要的。 - 在
IntentFactory
使用的名称Mode
类作为额外的名字。 - 将
IntentFactory
转换Mode
为String
使用name()
- 进入后
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; } }
回答by Ramakrishna Joshi
Most of the answers that are using Parcelable concept here are in Java code. It is much more easy in Kotlin. Just annotate enum with @Parcelizeand implement Parcelable interface.
这里使用 Parcelable 概念的大多数答案都在 Java 代码中。在 Kotlin 中这要容易得多。只需用@Parcelize注释枚举 并实现Parcelable接口。
@Parcelize
enum class ViewTypes : Parcelable {
TITLE, PRICES, COLORS, SIZES
}