255

I have an activity that when started needs access to two different ArrayLists. Both Lists are different Objects I have created myself.

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.

What are my options?

Can I pass an Enum?

As an aside: is there a way to pass parameters to an Activity Constructor from an Intent?

Bruno Bieri
  • 9,724
  • 11
  • 63
  • 92
jax
  • 37,735
  • 57
  • 182
  • 278

15 Answers15

646

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:

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 tedzyc about the fact that the solution provided by Oderik gives you an error. However, the alternative offered is a bit cumbersome to use (even using generics).

If you are really worried about the performance of adding the enum to an Intent I propose these alternatives instead:

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

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

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

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.

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.

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.

Usage:

// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()
pablisco
  • 14,027
  • 4
  • 48
  • 70
  • 15
    +1 for pointing out that it's a "real bad idea" to make them application wide. – bugfixr Nov 09 '12 at 20:18
  • 3
    I actually did work on a project where I just didn't want to deal with serialization or bundling objects (lots of objects with a lot of variables in them), and using static global variables was fine... until a teammate got onto the project. The cost of trying to coordinate the use of those globals made me go "screw it. I'm writing a code generator to make me some Parcelables". The number of bugs dropped significantly – Joe Plante May 07 '13 at 12:54
  • Furthermore, on the 'complicated' examples there is no external constant or casting so could be considered more convenient. One disadvantage is that you can only put one of the values in the enumeration. But the question was about keeping a state and generally we use one of them for these cases. There is room for improvement, like adding a cache to the Serializer but I think they are valid alternatives. – pablisco Jul 12 '13 at 09:17
  • The [Android docs](http://developer.android.com/reference/android/os/Parcel.html#writeSerializable%28java.io.Serializable%29) actually suggest avoiding Parcel.writeSerializable due to extremely large overhead. Just something to keep in mind.. – Coeffect Sep 09 '14 at 23:34
  • 3
    @Coeffect Yeah, it's an understandable suggestion, but in most cases this can be qualified as premature optimisation unless you are parsing thousands of enums (which by nature they should be only a few given that they are used to handle state) On a Nexus 4 you get 1ms improvement (http://www.developerphil.com/parcelable-vs-serializable/) not sure it's worth the extra leg work, but then again you have the other alternatives I suggested ;) – pablisco Sep 11 '14 at 14:42
  • In Kotlin enum don't seem to be serialisable. – rgv Mar 21 '18 at 00:06
  • 1
    @rgv Under the hood Kotlin cross compiles `enum class` types to a plain Java `enum`. I guess the easier workaround is to make the `enum class` implement `Serializable`: `enum class AwesomeEnum : Serializable { A, B, C }` Not ideal, but should work. – pablisco Mar 22 '18 at 11:23
  • I'm having a hard time understanding, if the first is correct, why bother with the rest? `public enum myEnum {A, B}` and doing `i.putExtra("myenum",myEnum.A);` + `myEnum num = (myEnum)i.getSerializableExtra("myenum");` this is all you need right? or do I need `public enum myEnum implements Serializable {A, B}` ? – Pierre Jun 27 '19 at 06:44
  • 1
    @Pierre as with any good answer, there is a "it depends". There is no need to extend Serialisable. The initial answer is valid. Those extra options are in case that you have a case where there may be a bottle neck, like if you are deserialising millions of records (hope not). Use what you see fit... – pablisco Jun 27 '19 at 07:04
  • +1. The Kotlin solution has a bit of problem for pending intents. Say unknowingly the order is changed (in the next iteration of the app) when the enum is de-serialized the caller will receive incorrect enum. So please add that note. – Gurupad Mamadapur Oct 16 '19 at 14:04
  • That's not coupling up your app, that's regular over engineering. All other options are still wide public stuff. – Misca Nov 04 '20 at 14:43
  • Absolute magic, simplest answer, worked OOB. `AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum"); ` – 80sTron Dec 01 '22 at 21:20
114

You can make your enum implement Parcelable which is quite easy for enums:

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

UPDATE: Please note wreckgar's comment that enum.values() allocates a new array at each call.

UPDATE: Android Studio features a live template ParcelableEnum that implements this solution. (On Windows, use Ctrl+J)

Oderik
  • 2,242
  • 2
  • 16
  • 25
  • 3
    It is also possible enum's toString() and valueOf() methods instead of ordinals. – Natix Nov 27 '12 at 00:12
  • That's true but why would you do that? For me the concept of Parcelables is a very cheap way of serialization. Using the ordinals for serialization is the cheapest way I can imagine regarding performance and memory consumption. If you don't care about efficiency you could also go with Serializable. – Oderik Jan 08 '13 at 08:10
  • 2
    Using ordinal() might break, when developer inserting new enum member. Of course, renaming enum member will break name() too. But, developers are more likely to insert new member, instead of renaming, as renaming requires him to re-factor the entire project. – Cheok Yan Cheng Mar 20 '13 at 12:12
  • 2
    I don't agree that additional (or reordered) enum values are more likely than renamed ones. Using a sophisticated IDE like IntelliJ IDEA refactoring is not a big deal. But your point is still good: you must make sure that the serialization is consistent throughout any coexistend implementation. That's true for any kind of serialisation. I presume in most cases parcelables are passed around whithin one app where only one implementation exists, so that should not be an issue. – Oderik Mar 22 '13 at 12:45
  • 2
    values() creates a new array on every call, so best to cache it in eg. a private static array – wreckgar23 Jan 30 '14 at 17:34
  • 2
    The fact that in general cases (such as db storage), ordinal() is not safe is not relevant for Android parcels. Parcels are not for long-term (persistent) storage. They die with the app. So when you add/rename an enum, you'll get new parcels. – noamtm Jun 14 '16 at 13:01
  • Important notes: `readFromParcel` should be empty or not change any fields, `writeToParcel` should only write *once* a unique value (int, string, etc.) that will be used to parse enum constant later, there is no need for private constructor which has `Parcel` parameter and `createFromParcel` should return the enum constant which parsed from the written unique value. You don't have to use `ordinal()`. – Eido95 Nov 22 '16 at 19:10
  • This is a problem in case of pending intents (say the order is changed). – Gurupad Mamadapur Oct 16 '19 at 14:08
  • @GurupadMamadapur implementation changes may be an issue for any kind of serialization. It's not a problem specific to this solution. I didn't investigate it, but I hope pending intents will be discarded during reinstallation of an app. If multiple apps are involved (or even if not) you proably shouldn't use parcelables for intent communication but stick to the system solutions using strings and ints and so on. – Oderik Oct 22 '19 at 09:34
28

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.

Cameron Lowell Palmer
  • 21,528
  • 7
  • 125
  • 126
22

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. :)

TURTLE
  • 229
  • 2
  • 2
  • 9
    or you can send it as a string (so it is readable) with Num.A.name() and then get it back using Num.ValueOf(intent.getStringExtra("TEST")) – Benoit Jadinon Sep 16 '11 at 08:04
  • 1
    I think Benoit's way is more secure, since temp.ordinal() is not preferred in practice because ordinal() value may change. See this post: http://stackoverflow.com/questions/2836256/passing-enum-or-object-through-an-intent-the-best-solution – Shnkc Nov 12 '14 at 13:03
15

If you really need to, you could serialize an enum as a String, using name() and valueOf(String), as follows:

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

Jan Berkel
  • 3,373
  • 1
  • 30
  • 23
4

Most of the answers that are using Parcelable concept here are in Java code. It is easier to do it in Kotlin.

Just annotate your enum class with @Parcelize and implement Parcelable interface.

@Parcelize
enum class ViewTypes : Parcelable {
TITLE, PRICES, COLORS, SIZES
}
Ramakrishna Joshi
  • 1,442
  • 17
  • 22
4

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.

  • 3
    Any Enum type extends Enum superclass by default, which already implements Serializable. – Arcao May 04 '14 at 16:54
2

about Oderik's post:

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

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

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

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));
Community
  • 1
  • 1
devzyc
  • 37
  • 1
  • 3
2

you can use enum constructor for enum to have primitive data type..

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.

niczm25
  • 169
  • 1
  • 2
  • 13
1

I like simple.

  • The Fred activity has two modes -- HAPPY and SAD.
  • Create a static IntentFactory that creates your Intent for you. Pass it the Mode you want.
  • The IntentFactory uses the name of the Mode class as the name of the extra.
  • The IntentFactory converts the Mode to a String using name()
  • Upon entry into onCreate use this info to convert back to a Mode.
  • You could use ordinal() and Mode.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;
        }
    }
    
JohnnyLambada
  • 12,700
  • 11
  • 57
  • 61
  • curious.. who calls the IntentFactory? can you elaborate on how a different activity would call Fred and how Fred can insure that the Mode is passed? – erik Apr 19 '17 at 19:36
0

Consider Following enum ::

public static  enum MyEnum {
    ValueA,
    ValueB
}

For Passing ::

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

To retrieve back from the intent/bundle/arguments ::

 MyEnum myEnum = (MyEnum) intent.getSerializableExtra("ENUM_CONST");
Wajid
  • 2,235
  • 1
  • 19
  • 29
muzz
  • 4,324
  • 2
  • 24
  • 14
0

I think your best bet is going to be to convert those lists into something parcelable such as a string (or map?) to get it to the Activity. Then the Activity will have to convert it back to an array.

Implementing custom parcelables is a pain in the neck IMHO so I would avoid it if possible.

Brad Hein
  • 10,997
  • 12
  • 51
  • 74
0

If you just want to send an enum you can do something like:

First declare an enum containing some value(which can be passed through 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;
        }
    }
}

Then:

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

And when you want to get it:

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

Piece of cake!

MHN
  • 11
  • 4
0

Use Kotlin Extension Functions

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

This gives you the flexibility to pass multiple of the same enum type, or default to using the class name.

// 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>()
Gibolt
  • 42,564
  • 15
  • 187
  • 127
-2

Don't use enums. Reason #78 to not use enums. :) Use integers, which can easily be remoted through Bundle and Parcelable.

hackbod
  • 90,665
  • 16
  • 140
  • 154
  • I use enums to standardize arbitrary types, normally for more readable switches, etc. Is it really that bad to pass the enum as a serializable and cast it when retrieving from the intent? I get that passing an integer instead of an enum should be safer and faster, but that way I'll never know what is the meaning of that number. – Draiken May 16 '13 at 15:36
  • 8
    @hackbod - what are the other 77 reasons? ;) Seriously though - there do seem to be many benefits to enum and they're not exactly difficult to 'remote' - any chance you can expand on your reasons against them? – ostergaard Aug 18 '13 at 03:05
  • 3
    @hackbod Please elaborate. If enums should not be used then remove them from the API. – dcow Aug 21 '13 at 18:39
  • 2
    Enums are part of the Java language specification, so they're a little tough to remove and still have a compliant Java implementation :) – tad Jan 15 '14 at 00:24
  • 1
    what about `mEnum.ordinal()`? it's return the position of the element – Saif Hamed Oct 15 '14 at 12:08
  • @SaifHamed, no. `Enum.ordinal()` is not reliable, see [@CommonsWare's comment](http://stackoverflow.com/questions/2836256/passing-enum-or-object-through-an-intent-the-best-solution#comment14348023_2836754) for elaboration. – naXa stands with Ukraine Dec 18 '14 at 09:38
  • 3
    The value of `Enum.ordinal()` is fixed at compile time. The only time that comment applies would be passing data between *apps* with different versions of the enum, or across app updates that change the order of elements within the enum. That sort of thing is dangerous whenever you use anything that's not a primitive. For passing intents between activities within a single app, `Enum.ordinal()` should be entirely safe. – Ian McLaird Feb 26 '15 at 18:21
  • @naXa that comment has been removed, do you remember what was in it? – TWiStErRob Apr 13 '16 at 12:43
  • @TWiStErRob, read IanMcLaird's comment above and try to guess =) – naXa stands with Ukraine Apr 13 '16 at 13:46