Passing enums through aidl interfaces

13.4k Views Asked by At

As enums aren't primitive types, what's the most effective way to pass an enum through an aidl interface in Android? Is there a way to convert the enum to an ordinal first?

3

There are 3 best solutions below

3
On

Non primitive types, other than String, require a directional indicator. Directional indicators include in, out and inout.

Take a look at the official documentation for that: http://developer.android.com/guide/developing/tools/aidl.html#aidlsyntax

Also, you can consider passing the String or ordinal representation of the enum and translate it back when needed. This is taken from the Effective Java 2nd edition:

// Implementing a fromString method on an enum type
private static final Map<String, Operation> stringToEnum = new HashMap<String, Operation>();
static { // Initialize map from constant name to enum constant
    for (Operation op : values())
        stringToEnum.put(op.toString(), op);
} // Returns Operation for string, or null if string is invalid
public static Operation fromString(String symbol) {
    return stringToEnum.get(symbol);
}

In the case above, Operation is an enum.


To get the ordinal of an enum consider this example:

public enum Badges{
    GOLD, SILVER, BRONZE;
}

// somewhere else:
int ordinal = Badges.SILVER.ordinal();// this should be 1
1
On

I simply use

String enumString = myEnum.name() 

(with MyEnum as enum and myEnum as value) to get the String representation and then

MyEnum myEnum = MyEnum.valueOf(enumString) 

to reconstruct the enum from the String representation.

Using Ordinals may be a wee bit faster but if I may add Enums later, this is more likely to break old code.

//Edit: As I don't like to have String as return type, I now implemented Parcellable like mentioned here: Passing enum or object through an intent (the best solution)

import android.os.Parcel; import android.os.Parcelable;

enum InitResponse implements Parcelable {
// Everything is fine.
SUCCESS,
// Something else
FOO;


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

@Override
public void writeToParcel(final Parcel dest, final int flags) {
    dest.writeString(name());
}

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

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

}
0
On

Yes, you can pass enums through AIDL, but you do have to implement Parcelable on the enum type.

1: A Parcelable implementation.

public enum RepeatMode implements Parcelable {
    NoRepeat,
    RepeatAll,
    RepeatTrack,
    ;

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(toInteger());
    }

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

    public static final Creator<RepeatMode> CREATOR = new Creator<RepeatMode>() {
        @Override
        public RepeatMode createFromParcel(Parcel in) {
            return RepeatMode.fromInteger(in.readInt());
        }

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

    public int toInteger() { return this.ordinal(); }
    public static RepeatMode fromInteger(int value)
    {
        return values()[value];
    }
}
  1. An import:

    RepeatMode.aidl: package com.cyberdyne.media;

    parcelable RepeatMode;

  2. And remember to mark enum arguments as in arguments.

Kind of obvious when you think about it. But I'm betting Google doesn't use a whole lot of enums in IBinder interfaces. I do it though. (Thank you Android studio for providing "Implement Parcelable", which doesn't work entirely for enums, but makes things relatively easy).

Discursus on Android enums:

Best practice recommendations against enums in Android were withdrawn many moons ago. You're trading about 200 bytes of executable for horrible horrible code. Phones have come a long way since Android 1.0. It's a no-brainer. Use enums. (Or use the insane Kotlin-driven attribute system that Google uses. Good luck with that.)

Official Java Lore dating back to the Original Java language spec discourages the use of naked ordinal(). The reasoning: maintainers in the deep future may unwittingly re-order the ordinals and break stuff. Frankly, I think that's pompous Java bogosity. I struggled with this for a long time, and after much soul-searching, I caved in.

public enum Badges {
   GOLD,SILVER, BRONZE; // Must match @array/badge_states 

   public int toInteger() { return this.ordinal(); }
   public static Badges fromInteger(int value) { return values()[value]);
}

If nothing else, it marks the class as one that probably persists integers. And a comment never hurts, and it makes the receiving end of marshalled enums a bit prettier (and just a tiny bit safer).