Android枚举字段序列化

实体类实现Parcelable接口后枚举字段该怎么写?正好碰到这个问题,记录一下。

枚举类

public enum EnumBean {
    STEAM,
    EPIC
}

枚举字段序列化,这里直接上写法。实际上用了AS插件Android Parcelable Code Generator生成,当然还是本着知其所以然的态度,思考一下为啥是这个写法。

public class Test implements Parcelable {
    public String name;

    public int age;

    public EnumBean e;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
        dest.writeInt(this.age);
        dest.writeInt(this.e == null ? -1 : this.e.ordinal());
    }

    public void readFromParcel(Parcel source) {
        this.name = source.readString();
        this.age = source.readInt();
        int tmpE = source.readInt();
        this.e = tmpE == -1 ? null : EnumBean.values()[tmpE];
    }

    public Test() {
    }

    protected Test(Parcel in) {
        this.name = in.readString();
        this.age = in.readInt();
        int tmpE = in.readInt();
        this.e = tmpE == -1 ? null : EnumBean.values()[tmpE];
    }

    public static final Parcelable.Creator<Test> CREATOR = new Parcelable.Creator<Test>() {
        @Override
        public Test createFromParcel(Parcel source) {
            return new Test(source);
        }

        @Override
        public Test[] newArray(int size) {
            return new Test[size];
        }
    };
}

其实从writeread字段e就可以看出,这里写入的是枚举类数组下标,读取时根据下标取出枚举类实体。

dest.writeInt(this.e == null ? -1 : this.e.ordinal());

this.e = tmpE == -1 ? null : EnumBean.values()[tmpE];

众所周知枚举中的每个声明都会创建枚举类对象,ordinal()也就是枚举中声明的顺序,对应到枚举类数组values()的下标。

看一下编译后的EnumBean.class文件字节码

  //枚举类默认继承Enum
  public final enum com/chenxuan/kotlin/EnumBean extends java/lang/Enum

  public final static enum Lcom/chenxuan/kotlin/EnumBean; STEAM

  public final static enum Lcom/chenxuan/kotlin/EnumBean; EPIC

  private final static synthetic [Lcom/chenxuan/kotlin/EnumBean; $VALUES

  //构造方法
  private <init>(Ljava/lang/String;I)V
    L0
    LINENUMBER 3 L0
    ALOAD 0
    ALOAD 1
    ILOAD 2
    INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
    RETURN

  //static块,创建声明的枚举类对象,枚举类数组。
  static <clinit>()V
   L0
    LINENUMBER 4 L0
    NEW com/chenxuan/kotlin/EnumBean
    DUP
    LDC "STEAM"
    ICONST_0
    INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
    PUTSTATIC com/chenxuan/kotlin/EnumBean.STEAM : Lcom/chenxuan/kotlin/EnumBean;
   L1
    LINENUMBER 5 L1
    NEW com/chenxuan/kotlin/EnumBean
    DUP
    LDC "EPIC"
    ICONST_1
    INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
    PUTSTATIC com/chenxuan/kotlin/EnumBean.EPIC : Lcom/chenxuan/kotlin/EnumBean;
   L2
    LINENUMBER 3 L2
    ICONST_2
    ANEWARRAY com/chenxuan/kotlin/EnumBean
    DUP
    ICONST_0
    GETSTATIC com/chenxuan/kotlin/EnumBean.STEAM : Lcom/chenxuan/kotlin/EnumBean;
    AASTORE
    DUP
    ICONST_1
    GETSTATIC com/chenxuan/kotlin/EnumBean.EPIC : Lcom/chenxuan/kotlin/EnumBean;
    AASTORE
    PUTSTATIC com/chenxuan/kotlin/EnumBean.$VALUES : [Lcom/chenxuan/kotlin/EnumBean;
    RETURN

  public static values()[Lcom/chenxuan/kotlin/EnumBean;
   L0
    LINENUMBER 3 L0
    GETSTATIC com/chenxuan/kotlin/EnumBean.$VALUES : [Lcom/chenxuan/kotlin/EnumBean;
    INVOKEVIRTUAL [Lcom/chenxuan/kotlin/EnumBean;.clone ()Ljava/lang/Object;
    CHECKCAST [Lcom/chenxuan/kotlin/EnumBean;
    ARETURN

  public static valueOf(Ljava/lang/String;)Lcom/chenxuan/kotlin/EnumBean;
   L0
    LINENUMBER 3 L0
    LDC Lcom/chenxuan/kotlin/EnumBean;.class
    ALOAD 0
    INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
    CHECKCAST com/chenxuan/kotlin/EnumBean
    ARETURN

很好理解,声明了两个EnumBean类型的变量STEAM和EPIC并在static块中初始化;同时声明了EnumBean数组$VALUES,按照枚举声明顺序添加到数组。

需注意枚举变量初始化时调用的构造方法传入了两个参数:声明的枚举变量对应的字符串STEAM、下标0。

    LDC "STEAM"
    ICONST_0
    INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V

EnumBean构造方法调用父类Enum的构造方法

  private <init>(Ljava/lang/String;I)V
    L0
    LINENUMBER 3 L0
    ALOAD 0
    ALOAD 1
    ILOAD 2
    INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
    RETURN

继续看Enum的构造方法

    private final String name;
    private final int ordinal;

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    public final int ordinal() {
        return ordinal;
    }

如此一来,ordinal就和枚举声明顺序或者说枚举数组下标联系起来了。

回看readFromParcelEnumBean.values()[tmpE],由上述字节码可知values()方法调用$VALUES.clone()也就是枚举类数组,然后根据数组下标取出枚举类实体。

还有我们常用的valueOf()方法,调用父类Enum.valueOf()传入了EnumBean.class

  public static valueOf(Ljava/lang/String;)Lcom/chenxuan/kotlin/EnumBean;
   L0
    LINENUMBER 3 L0
    LDC Lcom/chenxuan/kotlin/EnumBean;.class
    ALOAD 0
    INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
    CHECKCAST com/chenxuan/kotlin/EnumBean
    ARETURN

Enum.valueOf()

    public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                                String name) {
        Objects.requireNonNull(enumType, "enumType == null");
        Objects.requireNonNull(enumType, "name == null");
        T[] values = getSharedConstants(enumType);
        if (values == null) {
            throw new IllegalArgumentException(enumType.toString() + " is not an enum type.");
        }

        for (int i = values.length - 1; i >= 0; --i) {
            T value = values[i];
            if (name.equals(value.name())) {
                return value;
            }
        }
        throw new IllegalArgumentException(
                "No enum constant " + enumType.getCanonicalName() + "." + name);
    }

Enum.getSharedConstants()根据传入的class获取枚举类数组values

    public static <T extends Enum<T>> T[] getSharedConstants(Class<T> enumType) {
        return (T[]) sharedConstantsCache.get(enumType);
    }

    private static final BasicLruCache<Class<? extends Enum>, Object[]> sharedConstantsCache
            = new BasicLruCache<Class<? extends Enum>, Object[]>(64) {
        @Override protected Object[] create(Class<? extends Enum> enumType) {
            return enumValues(enumType);
        }
    };

    private static Object[] enumValues(Class<? extends Enum> clazz) {
        if (!clazz.isEnum()) {
            return null;
        }
        try {
            Method valueMethod = clazz.getDeclaredMethod("values");
            return (Object[]) valueMethod.invoke(null);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

这里通过反射获取到枚举类数组,然后迭代数组找到name相同的枚举类。而name其实就是声明的枚举变量对应的字符串,在static块中初始化时调用到父类Enum构造方法赋值。

    NEW com/chenxuan/kotlin/EnumBean
    DUP
    LDC "STEAM"
    ICONST_0
    INVOKESPECIAL com/chenxuan/kotlin/EnumBean.<init> (Ljava/lang/String;I)V
    PUTSTATIC com/chenxuan/kotlin/EnumBean.STEAM : Lcom/chenxuan/kotlin/EnumBean;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章