美文网首页Andorid
Android开发@IntDef完美替代Enum (枚举)

Android开发@IntDef完美替代Enum (枚举)

作者: 萨达哈鲁酱 | 来源:发表于2019-03-05 17:52 被阅读3次

    概要

    Enum 是 java 中一种包含固定常数的类型,当我们需要预先定义一些值时,我们使用 Enum,这样做通常为了在编译时期避免接受额外常量引起的错误。

    而且,Enum 增加了APK 的大小,比常量多5到10倍的内存占用,这是关于应用性能的最佳实践.

    使用 Enum 的缺点

    每一个枚举值都是一个对象,在使用它时会增加额外的内存消耗,所以枚举相比与 Integer 和 String 会占用更多的内存。

    较多的使用 Enum 会增加 DEX 文件的大小,会造成运行时更多的开销,使我们的应用需要更多的空间。

    如果你的应用使用很多的 Enum ,最好使用Integer 或 String 替代他们,但是这样还会有问题。

    既然都说到这个份上了,那么有什么比较好的解决方法呢?

    官方文档说明,安卓开发应避免使用Enum(枚举类),因为相比于静态常量Enum会花费两倍以上的内存。参考这里
    那么如果需要使用Enum应该怎么做呢?

    • 解决方案

    既然是因为参数的类型太泛了造成的类型不安全,那么我只要将参数限定在某一个类型集合里面,不就大功告成了?!

    是滴,一下就是要将的@IntDef/@StringDef + @interface来进行限定参数。


    使用注解库

    这些注解不是默认加载的,它们被包装为一个单独的库。Support Library现在是由一些更小的库组成的,包括:v4-support、appcompat、gridlayout、mediarouter等等。
    添加注解的最简单的方法就是打开Project Structure对话框。首先在左边选中module,然后右边选中Dependencies标签,点击“+”号按钮,选择Library Dependency。如果SDK中已经包括了Android Support库,那么注解支持库就会显示在快捷选择列表中了,只需要点击选择就可以。

    • 步骤1:点击Project Structure按钮


      这里写图片描述
    • 步骤2:选中Dependencies标签,点击“+”号按钮


      这里写图片描述
    • 步骤3:在下拉列表中选中support-annotations库


      这里写图片描述
    • 点击OK确定,这将会修改build.gradle文件。当然也可以手动在Gradle中添加如下依赖:

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

    应用

    • 定义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;
    
    • 定义一个IntDef注解,包含上面的常量,两种形式
    @IntDef({ADD,SUB,MUL,DIV})
    

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

    区别是第二种可以用条件进行位运算,更多详细信息,请参考官方文档

    • 定义一个注解,表明当前@IntDef的保留策略,只保留源码中,编译时删除,
    @Retention(RetentionPolicy.SOURCE)
    

    当然你还可以指定其他策略:

    Class:编译时被保留,在class文件中存在,但JVM将会忽略

    Runtime:将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用

    • 自定义一个注解 表明类型
    public @interface Operation{}
    
    • 使用,在方法中使用,类型安全,替代枚举
    public void operation(@Operation int opeartion) {
        switch (opeartion) {
          case ADD:
            break;
          case SUB:
            break;
          case DIV:
            break;
          case MUL:
            break;
        }
      }
    
    

    Android 中有使用到的一个例子

    Toast

    public class Toast {
        static final String TAG = "Toast";
        static final boolean localLOGV = false;
    
        /** @hide */
        /*定义部分*/
        @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;
    
        ...
    
        /*作为类型使用时*/
         /**
         * Set how long to show the view for.
         * @see #LENGTH_SHORT
         * @see #LENGTH_LONG
         */
        public void setDuration(@Duration int duration) {
            mDuration = duration;
        }
    
        /*做为返回值时*/
        /**
         * Return the duration.
         * @see #setDuration
         */
        @Duration
        public int getDuration() {
            return mDuration;
        }
    
    }
    
    • ps :这里是IntDef的API说明
    /*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})
    

    总结

    可以看到,如果不适用枚举,将会带来类型不安全的问题。一般情况下,我们在很多地方都会使用到枚举,因为方便和简洁。但是使用枚举也会产生占用内存过高等情况。所以我们可以有了自定义的方案,来限定我们使用的类型范围。

    相关文章

      网友评论

        本文标题:Android开发@IntDef完美替代Enum (枚举)

        本文链接:https://www.haomeiwen.com/subject/hliouqtx.html