Android development @ IntDef perfectly replaces Enum (enumeration)

outline

Enum is a type containing fixed constants in java. When we need to define some values in advance, we use enum. This is usually done to avoid errors caused by accepting additional constants at compile time.

Moreover, Enum increases the size of APK and uses 5 to 10 times more memory than constant, which is the best practice on application performance

Disadvantages of using Enum

Each enumeration value is an object, which will increase additional memory consumption when used, so enumeration will occupy more memory than Integer and String.

More use of Enum will increase the size of DEX files, cause more overhead at runtime, and make our applications need more space.

If your application uses a lot of enums, it's best to replace them with Integer or String, but there will be problems.

When it comes to this, what are the better solutions?

According to the official document, Android development should avoid using Enum (class), because Enum will cost more than twice the memory compared with the static constant. reference resources here
So what should I do if I need to use Enum?

  • Solution

Since the type is unsafe because the type of the parameter is too general, I just need to limit the parameter to a certain type set, and I'll be done?!

Yes, the next step is to use @ IntDef/@StringDef + @interface to qualify parameters.

Using annotation Libraries

These annotations are not loaded by default, they are packaged as a separate library. The Support Library now consists of smaller libraries, including V4 support, appcompat, gridlayout, mediarouter, and so on.
The easiest way to add annotations is to manually add the following dependencies in Gradle:

dependencies {  
    compile 'com.android.support:support-annotations:23.1.0'  
} 

application

  • Define constants for static final
private static final int ADD = 0;
private static final int SUB = 1;
private static final int MUL = 2;
private static final int DIV = 3;
  • Define an IntDef annotation, including the above constants, in two forms
@IntDef({ADD,SUB,MUL,DIV})

or

@IntDef(flag = true, value = {ADD,SUB,MUL,DIV})

The difference is that the second method can perform bit operation with conditions. For more details, please refer to Official documents

  • Define an annotation to indicate that the current @ IntDef retention policy is only retained in the source code and deleted during compilation,
@Retention(RetentionPolicy.SOURCE)

Of course, you can also specify other strategies:

Class: it is reserved at compile time and exists in the class file, but the JVM will ignore it

Runtime: will be reserved by the JVM, so they can be read and used by the JVM or other code using reflection mechanism at runtime

  • Customize an annotation to indicate the type
public @interface Operation{}
  • Use, use in method, type safe, instead of enumeration
public void operation(@Operation int opeartion) {
    switch (opeartion) {
      case ADD:
        break;
      case SUB:
        break;
      case DIV:
        break;
      case MUL:
        break;
    }
  }

There is an example used in Android

Toast

public class Toast {
    static final String TAG = "Toast";
    static final boolean localLOGV = false;

    /** @hide */
    /*Definition part*/
    @IntDef({LENGTH_SHORT, LENGTH_LONG})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Duration {}

    public static final int LENGTH_SHORT = 0;
    public static final int LENGTH_LONG = 1;

    ...

    /*When used as a type*/
     /**
     * Set how long to show the view for.
     * @see #LENGTH_SHORT
     * @see #LENGTH_LONG
     */
    public void setDuration(@Duration int duration) {
        mDuration = duration;
    }

    /*As a return value*/
    /**
     * Return the duration.
     * @see #setDuration
     */
    @Duration
    public int getDuration() {
        return mDuration;
    }

}
  • ps: here is the API description of IntDef
/*IntDef
implements Annotation
android.support.annotation.IntDef
Class Overview
Denotes that the annotated element of integer type, represents a logical type and that its value should be one of the explicitly named constants. If the IntDef#flag() attribute is set to true, multiple constants can be combined.
*/

//Example:

@Retention(SOURCE)
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
  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;
  ...
  public abstract void setNavigationMode(@NavigationMode int mode);
  @NavigationMode
  public abstract int getNavigationMode();

//For a flag, set the flag attribute:
@IntDef(
      flag = true
      value = {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})

summary

It can be seen that if enumeration is not applicable, it will lead to the problem of type insecurity. In general, we will use enumeration in many places because it is convenient and concise. However, the use of enumeration will also lead to high memory consumption. So we can have a custom scheme to limit the range of types we use.

Keywords: Android

Added by methyl_blue on Fri, 04 Feb 2022 11:16:36 +0200