ReverseBits
Back to Blogs
Mobile2017-01-21

Android IntDef/StringDef: Type Safety Without Enum Overhead

T
Tapan ParmarAuthor
1 reads

TL;DR

Android enums cost 13x more memory than IntDef constants. For apps targeting low-end devices or optimizing APK size, replace enums with @IntDef and @StringDef annotations. You get compile-time type checking without runtime overhead. Migration: Create constant class, add @IntDef annotation, replace enum references. Kotlin: Use @JvmStatic with companion objects.

The “Magic constants”

Ever thought that why we always have to pass predefined values while calling some methods in android such as void setVisibility(int visibility) always take only these parameters VISIBLE, INVISIBLE, GONE ? Although the argument is integer, we can’t simply pass 0 or 1 or any other values in it,

view.setVisibility(View.VISIBLE) //View.GONE

Why so?

In Java, enum is a known concept, and in many cases you can use it, but for Android, enum is something you should avoid using as its processing performance is not efficient, so in Android performance patterns it’s recommended to avoid enums and use annotations like @IntDef and @StringDef.


Example

Suppose you are working on a library that make user choose photo from gallery and camera and you are passing int value in your library method and taking argument in integer, based upon that you are opening camera or gallery.

Your simple method But stop a minute and think, what if the person use your library pass any integer values other then 1 and 0? your code will face big defeat, To prevent this kind of situations android has intDef and stringDef annotations.

Although enum is a also a way of doing it but why shouldn’t we use it? it’s a whole topic so we will blame enums for being freaking bad at last.


How we can use intDef and stringDef?

below is a simplest example how previously described problem can be made easy with intDef

@Retention(RetentionPolicy.SOURCE) //declare retention policy source i.e compile time
@IntDef({OPEN_CAMERA, OPEN_GALLERY}) //intDef value allocation

//Declare interface which we gonna use as our custom annotation
public @interface NavigationMode {}

// Must be static final int values for intDef
public static final int OPEN_CAMERA = 0;
public static final int OPEN_GALLERY = 1;

private int mode;

//As setNavigationMode has argument type of @NavigationMode, parameters passed can only be
// OPEN_CAMERA or OPEN_GALLERY
public void setNavigationMode(@NavigationMode int mode){
    this.mode = mode;
}

@NavigationMode
public int getNavigationMode(){
    return OPEN_CAMERA || OPEN_GALLERY; //Here you can't return simple integer value, must match @NavigationMode
}

Now let’s break it apart, line#1 @Retention is called for declaring retention policy, A retention policy determines at what point annotation should be discarded.

  • RetentionPolicy.SOURCE: Discard during the compile. These annotations don't make any sense after the compile has completed, so they aren't written to the bytecode. Example: @Override, @SuppressWarnings

  • RetentionPolicy.CLASS: Discard during class load. Useful when doing bytecode-level post-processing. Somewhat surprisingly, this is the default.

  • RetentionPolicy.RUNTIME: Do not discard. The annotation should be available for reflection at runtime. Example: @Deprecated

IntDef are the specific annotations where you can pass the group of constant values that can be used for your purpose like in above gist we have used OPEN_CAMERA and OPEN_GALLERY in argument of @intDef.


Usage

@NavigationMode is a name of interface which is used as reference for our methods on which we actually want to apply our intDef values, like see the getter and setter where we used it.

Here is simplest example made by us, BTW it’s our first video ever published. This video shows how all things work.


Why not enums?

The important topic here to understand is why we are not using enums? Although you can use it for similar purposes but they will not as memory efficient as static integer constants the very nice video of performance optimization discussing the similar topic is here,

The guy given an overview of how optimization can be done by avoiding enums but stop thinking of things like Enums/ArrayMaps/Iterator fixes as Premature optimizations. That’s really the wrong mentality. This is “Preventative Optimizations”.

Here is a very basic representation of comparison of both of them, so if you are really wanna be careful about memory because it can attack anytime on your app and you have no control over it, but small steps like this can really save your ass from being kicked.


Notes

We haven’t discussed about stringDef here but the implementation above can be done with static final String constants also, with @stringDef annotations.


Helpful discussions on this topic

Infinium blog on this topic

Android developer docs

Stack overflow post on different Retention Policies

Related Topics

AndroidAndroid App DevelopmentAndroid AnnotationsMobile App DevelopmentMobile Apps

Enjoyed this article?

Check out more blogs on our blog.

Read More Blogs

Related Blogs