Passing enums through aidl interfaces
Asked Answered
C

3

8

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?

Concinnate answered 8/2, 2011 at 18:44 Comment(5)
Take a look at Charlie Collins' comment. That's completely true: avoid enums as much as possible.Gertrude
@Gertrude that needs to be taken with a grain of salt. enums are good design. there's a reason why android doesn't remove enums from the language.Crewel
That's true dude. However, I think they didn't move it because it's not that easy. They still use javac before dexing the bytecode. Anyway... I love enums, and use them when they make things clear and elegant.Gertrude
Enums are perfectly fine in app code. Framework authors operate under a different set of constraints. (Compare, for instance, how MFC avoids virtuals in many of its core classes.) For Android specifically, early tests showed that enums consumed significantly more memory than constants, so the framework avoids them. YMMV. In fact, your mileage will vary, so do the right thing for your app rather than just doing what everybody else is doing.Denten
Somewhere in the deep web, Google gave us the go-ahead to use enums in Android code. Somewhere in the neighborhood of 600 bytes for an enum. And phones have come a long way since Android 1.0. Go for it.Caesaria
A
9

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];
    }
};

}
Android answered 20/5, 2012 at 20:47 Comment(1)
I now ended up combinding this approach with the Parcellable-Implementation from #2836756 resulting in the edited code above.Android
G
5

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
Gertrude answered 8/2, 2011 at 18:57 Comment(3)
You should also consider avoiding enums entirely (in an Android project), if you can help it. developer.android.com/guide/practices/design/…Snort
Unfortunately I don't have the option of avoiding enums; this is a collaborative project. Can I extract the ordinal of an enum value?Concinnate
@CharlieCollins That advice has been taken back.Ehtelehud
C
5

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).

Caesaria answered 21/4, 2019 at 3:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.