95

I used to define a set of related constants like Bundle keys together in an interface like below:

public interface From{
    String LOGIN_SCREEN = "LoginSCreen";
    String NOTIFICATION = "Notification";
    String WIDGET = "widget";
}

This provides me a nicer way to group related constants together and used them by making a static import (not implements). I know Android framework also uses the constants in same way like Toast.LENTH_LONG, View.GONE.

However, I often feel that the Java Enums provide much better and powerful way to represent the constant.

But is there a performence issue in using enums on Android?

With a bit of research I ended up in confusion. From this question "Avoid Enums Where You Only Need Ints” removed from Android's performance tips? it's clear that Google has removed "Avoid enums" from its performance tips, but from it's official training docs Be aware of memory overhead section it clearly says: "Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android." Is this still holds good? (say in Java versions after 1.6)

One more issue that I observed is to send enums across intents using Bundle I should send them by serializing (i.e putSerializable(), that I think an expensive operation compared to primitive putString() method, eventhough enums provides it for free).

Can someone please clarify which one is the best way to represent the same in Android? Should I strictly avoid using enums on Android?

Community
  • 1
  • 1
nvinayshetty
  • 3,187
  • 1
  • 21
  • 32
  • 5
    You should use the tools that you have available. In fact, an activity or fragment takes up a lot of memory and cpu usage, but that's no reason to stop using them. Use a static int if you only need that, and use enums when you need those. – Patrick Mar 21 '15 at 15:01
  • 11
    I agree. This smells like premature optimization. Unless you are having a performance and/or memory issue, and can prove through profiling that enums are the cause, use them where it makes sense. – GreyBeardedGeek Mar 21 '15 at 17:24
  • 2
    It used to be believed that enums incurred a non-trival performance penalty, but more recent benchmarks show no benefit to using constants instead. See http://stackoverflow.com/questions/24491160/when-to-use-enum-int-constants as well as http://stackoverflow.com/questions/5143256/why-was-avoid-enums-where-you-only-need-ints-removed-from-androids-performanc – Benjamin Sergent Apr 22 '15 at 18:50
  • As you can see yourself from the link, they are not inherently flawed, but they do cost memory, which is still a potential concern in the aggregate if not necessarily in a given limited usage on a modern device. So use them if they are a particularly elegant fit for what you want to do, but preferably use numeric constants if those are a reasonable solution. What is the purpose of asking a new question on this? – Chris Stratton Apr 22 '15 at 21:00
  • First of all we use interfaces to implement not to extend, that's why you shouldn't create/declare any type of variables inside interface rather than final and static. – Haris Qurashi Apr 29 '15 at 12:45
  • 1
    To avoid the performance penalty of serializing an Enum in a Bundle, you can pass it as an int using `Enum.ordinal()` instead. – BladeCoder Jul 22 '15 at 10:37
  • 1
    finally there is some explanations on performance problems on Enum here https://www.youtube.com/watch?v=Hzs6OBcvNQE – nvinayshetty Aug 11 '15 at 08:52

7 Answers7

119

Use enum when you need its features. Don't avoid it strictly.

Java enum is more powerful, but if you don't need its features, use constants, they occupy less space and they can be primitive itself.

When to use enum:

  • type checking - you can accept only listed values, and they are not continuous (see below what I call continuous here)
  • method overloading - every enum constant has its own implementation of a method

    public enum UnitConverter{
        METERS{
            @Override
            public double toMiles(final double meters){
                return meters * 0.00062137D;
            }
    
            @Override
            public double toMeters(final double meters){
                return meters;
            }
        },
        MILES{
            @Override
            public double toMiles(final double miles){
                return miles;
            }
    
            @Override
            public double toMeters(final double miles){
                return miles / 0.00062137D;
            }
        };
    
        public abstract double toMiles(double unit);
        public abstract double toMeters(double unit);
    }
    
  • more data - your one constant contains more than one information that cannot be put in one variable

  • complicated data - your constant need methods to operate on the data

When not to use enum:

  • you can accept all values of one type, and your constants contain only these most used
  • you can accept continuous data

    public class Month{
        public static final int JANUARY = 1;
        public static final int FEBRUARY = 2;
        public static final int MARCH = 3;
        ...
    
        public static String getName(final int month){
            if(month <= 0 || month > 12){
                throw new IllegalArgumentException("Invalid month number: " + month);
            }
    
            ...
        }
    }
    
  • for names (like in your example)
  • for everything else that really doesn't need an enum

Enums occupy more space

  • a single reference to an enum constant occupies 4 bytes
  • every enum constant occupies space that is a sum of its fields' sizes aligned to 8 bytes + overhead of the object
  • the enum class itself occupies some space

Constants occupy less space

  • a constant doesn't have a reference so it's a pure data (even if it's a reference, then enum instance would be a reference to another reference)
  • constants may be added to an existing class - it's not necessary to add another class
  • constants may be inlined; it brings extended compile-time features (such as null checking, finding dead code etc.)
Kamil Jarosz
  • 2,168
  • 2
  • 19
  • 30
  • A single reference to an int constant (copy of the int itself) also occupies 4 bytes. – BladeCoder Jul 22 '15 at 10:26
  • @BladeCoder reference to enum instance occupy 4 bytes, but instance occupy additional space itself, it's much more – Kamil Jarosz Jul 22 '15 at 10:28
  • 2
    You can use the @IntDef annotation to simulate type checking for int constants. It's one less argument in favor of Java Enums. – BladeCoder Jul 22 '15 at 10:29
  • Of course the Enums take up much more memory than the int constants, I was just wondering why you mentioned the references since they occupy the same space in both cases. – BladeCoder Jul 22 '15 at 10:32
  • 3
    @BladeCoder no, you don't need a reference to constant – Kamil Jarosz Jul 22 '15 at 10:38
  • You can check this video for more info: https://www.youtube.com/watch?v=Hzs6OBcvNQE – Ultimo_m Aug 25 '15 at 17:33
  • 1
    Also, it's good to note that using enums promotes usage of reflection. And it's known that usage of reflection is a HUGE performance hit for Android. See here: http://blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html – w3bshark May 05 '16 at 13:16
  • 4
    @w3bshark How do enums promote the use of reflection? – Kevin Krumwiede Jun 23 '17 at 18:52
  • 3
    Another thing to keep in mind is that a heap dump from the standard empty "Hello, world!" project includes over 4,000 classes and 700,000 objects. Even if you had a ludicrously huge enum with thousands of constants, you can be sure that the performance effect would be negligible next to the bloat of the Android framework itself. – Kevin Krumwiede Jun 23 '17 at 18:59
  • Jake Wharton on the subject: https://twitter.com/jakewharton/status/551876948469620737?lang=en – Maksim Turaev Aug 24 '17 at 13:46
59

If the enums simply have values, you should try to use IntDef/StringDef , as shown here:

https://developer.android.com/studio/write/annotations.html#enum-annotations

Example: instead of :

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

you use:

@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}

public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;

and in the function that has it as a parameter/returned value , use:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

In case the enum is complex, use an enum. It's not that bad.

To compare enums vs constant values, you should read here:

http://hsc.com/Blog/Best-Practices-For-Memory-Optimization-on-Android-1

Their example is of an enum with 2 values. It takes 1112 bytes in dex file compared to 128 bytes when constant integers are used . Makes sense, as enums are real classes, as opposed to how it works on C/C++ .

cambunctious
  • 8,391
  • 5
  • 34
  • 53
android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Although I appreciate the other answers providing pros/cons of enums, this answer should be the accepted answer since it provides the best solution for Android, specifically. Support annotations are the way to go. We, as Android developers should be thinking "unless I have a strong reason to use enums, I will use constants with annotations", rather than the other answers' way of thinking "unless I start to get worried about memory/performance later on, I can use enums". Stop yourself now from having performance problems later on! – w3bshark May 05 '16 at 13:09
  • @w3bshark It's still ok to use enums, but this is a nice alternative if you wish to save on memory and maybe a bit better performance. – android developer May 07 '16 at 10:08
  • 3
    @w3bshark If you are having performance problems, enums are unlikely to be the first thing that you should be thinking of to solve them. Annotations have their own trade-offs: they don't have compiler support, they can't have their own fields and methods, they are tedious to write, you can easily forget to annotate an int, and so on. They aren't a better solution overall, they are just there to save heap space when you need to. – Malcolm Jan 01 '17 at 14:22
  • @Malcolm The IDE supports them well, and remembering how to use something is a part of learning. But you are correct on the rest. It is annoying to use. Google usually don't use enums, and use constants instead. I think that if you want max performance, you can use this solution. But if the app works fine as it is, I would just let it be with enums. – android developer Jan 01 '17 at 23:55
  • @androiddeveloper If remembering how to use something were so easy, we wouldn't have use-after-free, buffer overflows, and other correctness problems. Unfortunately, even experienced programmers can forget something, so the less things to remember the better. Compiler support is better because it doesn't depend on IDE settings or even on IDE being present at all. This applies to a CI environment, for example. As for Google, they were writing Android during the times when resources were much more constrained than today and with a poor VM, so they needed all these tricks much more than we do. – Malcolm Jan 02 '17 at 08:22
  • @Malcolm They still code this way. There are very few enums, if any, in the Android framework. And I agree that it's easier and shorter to use them. About compiler support, what exactly is it that you look for? The constants are just numbers. Do you mean for debugging, to show the value in a readable way? – android developer Jan 02 '17 at 09:26
  • @androiddeveloper Google now has watches, maybe some other small devices in the future, so they still have a reason to do that. Regarding compilers, I want my code to fail at compilation if there is something incorrect with it. If a developer makes a mistake with enums, the code won't compile anywhere: neither in an IDE, nor on a CI build agent. If a developer makes a mistake with an annotated argument, he can ignore the mistake, push, and a CI agent will happily compile it. Of course, there are lint checks, code reviews, and so on, but I'd rather avoid creating the problem in the first place. – Malcolm Jan 02 '17 at 09:43
  • @Malcolm If you use the Android Studio IDE, it will help you to prevent from making weird mistakes. I don't understand what watches have to do with it. Again, I suggest using whatever suits you. I use enums too. – android developer Jan 02 '17 at 12:30
  • @androiddeveloper Helping to prevent and making sure that they are _never_ made are two different things. Regarding watches, I meant Android Wear watches as they are obviously a lot more constrained than smartphones. – Malcolm Jan 02 '17 at 17:15
  • @Malcolm Making mistakes is not something the compiler can prevent, no matter what the developer chooses. You can make mistakes with enums too, and if you don't, someone else can. I still don't get what Android Wear has to do with enums. – android developer Jan 03 '17 at 17:05
  • 1
    @androiddeveloper You can't make a mistake by passing a constant not defined in the enum declaration. This code won't ever compile. If you pass a wrong constant with an `IntDef` annotation, it will. As to the watches, I think it's pretty clear too. Which device has more CPU power, RAM, and battery: a phone or a watch? And which ones need software to be more optimized for performance because of that? – Malcolm Jan 03 '17 at 19:06
  • @Malcolm If you pass a wrong enum value, it's a mistake. Nothing can help with that. Not even the best compiler or IDE. About watches, ok, but this isn't about them. Android include much more than watches. – android developer Jan 03 '17 at 20:04
  • 3
    @androiddeveloper OK, if you insist on strict terminology, by mistakes I meant errors which are not compile-time errors (run-time or program-logic errors). Are you trolling me or really don't get the difference between them and the benefits of compile-time errors? This is a well-discussed topic, look this up on the web. Regarding watches, Android does include more devices, but the most constrained ones will be the lowest common denominator. – Malcolm Jan 03 '17 at 21:30
  • @Malcolm All I'm saying is that nothing can help with preventing all mistakes. You should use Android Studio, and it will help you with both enums and annotations. If you use both well, there is no reason to make weird mistakes. – android developer Jan 04 '17 at 22:03
  • 2
    @androiddeveloper I've already responded to both of those statements, repeating them once more doesn't invalidate what I said. – Malcolm Jan 04 '17 at 22:09
  • @Malcolm Good, so we both agree that each of those techniques has its own advantages and disadvantages, and each person should choose what he prefers. – android developer Jan 05 '17 at 10:36
14

With Android P, google has no restriction/objection in using enums

The documentation has changed where before it was recommended to be cautious but it doesn't mention it now. https://developer.android.com/reference/java/lang/Enum

Ali
  • 2,427
  • 22
  • 25
  • 1
    Is there any other evidence besides this: I found @JakeWharton statement about that: https://twitter.com/jakewharton/status/1067790191237181441. Anyone can check bytecode. – soshial Oct 16 '19 at 05:27
13

In addition to previous answers, I would add that if you are using Proguard (and you should definitely do it to reduce size and obfuscate your code), then your Enums will be automatically converted to @IntDef wherever it is possible:

https://www.guardsquare.com/en/proguard/manual/optimizations

class/unboxing/enum

Simplifies enum types to integer constants, whenever possible.

Therefore, if you have some discrete values and some method should allow to take only this values and not others of the same type, then I would use Enum, because Proguard will make this manual work of optimizing code for me.

And here is a good post about using enums from Jake Wharton, take a look at it.

As a library developer, I recognize these small optimizations that should be done as we want to have as little impact on the consuming app's size, memory, and performance as possible. But it's important to realize that [...] putting an enum in your public API vs. integer values where appropriate is perfectly fine. Knowing the difference to make informed decisions is what's important

Gaket
  • 6,533
  • 2
  • 37
  • 67
11

Should I strictly avoid using enums on Android?

No. "Strictly" means they are so bad, they should not be used at all. Possibly a performance issues might arise in an extreme situation like many many many (thousands or millions of) operations with enums (consecutive on the ui thread). Far more common are the network I/O operations that should strictly happen in a background thread. The most common usage of enums is probably some kind of type check - whether an object is this or that which is so fast you won't be able to notice a difference between a single comparison of enums and a comparison of integers.

Can someone please clarify which one is the best way to represent the same in Android?

There is no general rule of thumb for this. Use whatever works for you and helps you get your app ready. Optimize later - after you notice there's a bottleneck that slows some aspect of your app.

stan0
  • 11,549
  • 6
  • 42
  • 59
  • 1
    Why would you optimize later, when it's just as easy to use constants with support annotations and optimize now? See @android_developer's answer here. – w3bshark May 05 '16 at 13:12
2

I like to add, that you can not use @Annotations when you declare a List<> or Map<> where either key or value is of one of your annotation interfaces. You get the error "Annotations are not allowed here".

enum Values { One, Two, Three }
Map<String, Values> myMap;    // This works

// ... but ...
public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;

@Retention(RetentionPolicy.SOURCE)
@IntDef({ONE, TWO, THREE})
public @interface Values {}

Map<String, @Values Integer> myMap;    // *** ERROR ***

So when you need to pack it into a list/map, use enum, as they can be added, but @annotated int/string groups can not.

Grisgram
  • 3,105
  • 3
  • 25
  • 42
2

Two facts.

1, Enum is one of the most powerful feature in JAVA.

2, Android phone usually has a LOT of memory.

So my answer is NO. I will use Enum in Android.

Kai Wang
  • 3,303
  • 1
  • 31
  • 27
  • 3
    If Android has LOT of memory, that doesn't mean your app should consume it all and leave none for others. You should follow the best practices in order to save your app from getting killed by Android OS. I am not saying do not use enums, but use only when you have no alternative. – Varundroid Jun 29 '17 at 00:21
  • 1
    I agree with you that we should follow the best practice, however, the best practice doesn't always means using least memory. Keeping code clean, easy to understanding is far more important than saving several k memory. You need to find a balance. – Kai Wang Jun 29 '17 at 18:51
  • 1
    I agree with you that we need to find a balance but using TypeDef isn't gonna make our code look bad or less maintainable. I am using it for more than a year now and never felt like my code isn't easy to understand, also none of my peers ever complained that TypeDefs are hard to understand as compared to enums. My advise is to use enums only when you have no other option but avoid it if you can. – Varundroid Jun 29 '17 at 23:34