web-dev-qa-db-fra.com

Devrais-je strictement éviter d'utiliser des enums sur Android?

J'avais l'habitude de définir un ensemble de constantes liées telles que les clés Bundle dans une interface comme ci-dessous:

public interface From{
    String LOGIN_SCREEN = "LoginSCreen";
    String NOTIFICATION = "Notification";
    String WIDGET = "widget";
}

Cela me fournit un moyen plus agréable de regrouper des constantes liées et de les utiliser en effectuant une importation statique (sans implémentation). Je sais que le framework Android utilise également les constantes de la même manière que Toast.LENTH_LONG, View.GONE.

Cependant, j’ai souvent l’impression que le Java Enums Constitue un moyen bien meilleur et puissant pour représenter la constante.

Mais existe-t-il un problème de performance lié à l'utilisation de enums sur Android?

Avec un peu de recherche, je me suis retrouvé dans la confusion. A partir de cette question "Evitez les domaines où vous avez seulement besoin d'Ints" supprimé des conseils de performance d'Android? il est clair que Google a supprimé "Évitez les enums" de ses conseils de performance, mais de sa documentation d’entraînement officielle Soyez conscient de la surcharge de mémoire La section indique clairement: "Les énums nécessitent souvent plus que deux fois plus de mémoire que les constantes statiques. Evitez strictement d'utiliser des enums sur Android. " Est-ce toujours valable? (Dans les versions Java après la 1.6)

Un autre problème que j’ai observé est d’envoyer enums à travers intents en utilisant Bundle, je devrais les envoyer en sérialisant (c.-à-d. putSerializable(), ce qui me opération comparée à la méthode primitive putString(), bien que enums l'ait fournie gratuitement).

Quelqu'un peut-il clarifier quel est le meilleur moyen de représenter la même chose dans Android? Devrais-je strictement éviter d'utiliser enums sur Android?

89
nvinayshetty

Utilisez enum lorsque vous avez besoin de ses fonctionnalités. Ne pas éviter strictement .

Java enum est plus puissant, mais si vous n’avez pas besoin de ses fonctionnalités, utilisez des constantes, elles occupent moins d’espace et peuvent être primitives elles-mêmes.

Quand utiliser enum:

  • vérification du type - vous pouvez accepter uniquement les valeurs répertoriées et elles ne sont pas continues (voir ci-dessous ce que j'appelle continu ici)
  • surcharge de la méthode - chaque constante enum a sa propre implémentation d'une méthode

    public enum UnitConverter{
        METERS{
            @Override
            public double toMiles(final double meters){
                return meters * 0.00062137D;
            }
    
            @Override
            public double toMeters(final double meters){
                return meters;
            }
        },
        MILES{
            @Override
            public double toMiles(final double miles){
                return miles;
            }
    
            @Override
            public double toMeters(final double miles){
                return miles / 0.00062137D;
            }
        };
    
        public abstract double toMiles(double unit);
        public abstract double toMeters(double unit);
    }
    
  • plus de données - votre constante contient plus d'une information qui ne peut pas être mise dans une variable

  • données compliquées - votre besoin constant de méthodes pour exploiter les données

Quand pas utiliser enum:

  • vous pouvez accepter toutes les valeurs d'un type et vos constantes ne contiennent que celles les plus utilisées
  • vous pouvez accepter des données continues

    public class Month{
        public static final int JANUARY = 1;
        public static final int FEBRUARY = 2;
        public static final int MARCH = 3;
        ...
    
        public static String getName(final int month){
            if(month <= 0 || month > 12){
                throw new IllegalArgumentException("Invalid month number: " + month);
            }
    
            ...
        }
    }
    
  • pour les noms (comme dans votre exemple)
  • pour tout ce qui n'a pas vraiment besoin d'un enum

Les enums occupent plus d'espace

  • une seule référence à une constante d’énumération occupe 4 octets
  • chaque constante d'énumération occupe un espace somme des tailles de ses champs aligné sur 8 octets + surcharge de l'objet
  • la classe d'énumération elle-même occupe de l'espace

Les constantes occupent moins d'espace

  • une constante n'a pas de référence, donc c'est une donnée pure (même s'il s'agit d'une référence, alors enum instance serait une référence à une autre référence)
  • des constantes peuvent être ajoutées à une classe existante - il n'est pas nécessaire d'ajouter une autre classe
  • les constantes peuvent être en ligne; il apporte des fonctionnalités étendues de compilation (telles que la vérification de nullité, la recherche de code mort, etc.)
105
Kamil Jarosz

Si les énumérations ont simplement des valeurs, vous devriez essayer d'utiliser IntDef/StringDef, comme indiqué ici:

http://tools.Android.com/tech-docs/support-annotations

Exemple: au lieu de:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

tu utilises:

@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
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;

et dans la fonction qui l'a en tant que paramètre/valeur renvoyée, utilisez:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

Si l'énumération est complexe, utilisez une enum. C'est pas si mal.

Pour comparer les énumérations avec les valeurs constantes, vous devez lire ici:

http://hsc.com/Blog/Best-Practices-For-Memory-Optimization-on-Android-1

Leur exemple est d'une enum avec 2 valeurs. Il faut 1112 octets dans le fichier dex comparé à 128 octets lorsque des entiers constants sont utilisés. Cela a du sens, car les énumérations sont de vraies classes, par opposition à la façon dont cela fonctionne en C/C++.

56
android developer

En plus des réponses précédentes, j’ajouterais que si vous utilisez Proguard (et que vous devez absolument le faire pour réduire la taille et masquer votre code), votre Enums sera automatiquement converti en @IntDef partout où c'est possible:

https://www.guardsquare.com/en/proguard/manual/optimizations

class/unboxing/enum

Simplifie les types enum en constantes entières, dans la mesure du possible.

Par conséquent, si vous avez des valeurs discrètes et qu'une méthode doit permettre de ne prendre que ces valeurs et pas d'autres du même type, j'utiliserais alors Enum, car Proguard me permettra d'optimiser le code manuellement.

Et voici un bon article sur l’utilisation des enums de Jake Wharton, jetez-y un coup d’œil.

En tant que développeur de bibliothèque, je reconnais ces petites optimisations à effectuer car nous souhaitons avoir le moins d'impact possible sur la taille, la mémoire et les performances de l'application consommatrice. Mais il est important de réaliser que placer une [...] énumération dans votre API publique par rapport aux valeurs entières, le cas échéant, convient parfaitement. Il est important de connaître la différence pour prendre des décisions en connaissance de cause

11
Gaket

Devrais-je strictement éviter d'utiliser des enums sur Android?

Non. "Strictly" signifie qu'ils sont si mauvais qu'ils ne devraient pas être utilisés du tout. Des problèmes de performances peuvent éventuellement survenir dans une situation extrême, telle que beaucoup beaucoup (des milliers ou des millions d'opérations) avec des énumérations (consécutives sur le thread ui). Beaucoup plus courantes sont les opérations d'E/S réseau qui devraient strictement se dérouler dans un thread en arrière-plan. L'utilisation la plus courante d'énums est probablement une sorte de vérification de type - si un objet est this ou that qui est si rapide que vous ne pourrez pas remarquer une différence entre une seule comparaison d'énums et une comparaison d'entiers.

Quelqu'un peut-il s'il vous plaît préciser quel est le meilleur moyen de représenter la même chose dans Android?

Il n'y a pas de règle générale pour cela. Utilisez tout ce qui vous convient et vous aide à préparer votre application. Optimiser plus tard - après avoir constaté un goulot d'étranglement qui ralentit certains aspects de votre application.

11
stan0

Avec Android P, Google n’a aucune restriction/objection à utiliser des énumérations.

La documentation a changé là où auparavant il était recommandé de faire preuve de prudence, mais elle ne le mentionne pas maintenant. https://developer.Android.com/reference/Java/lang/Enum

8
Ali

Deux faits.

1, Enum est l’une des fonctionnalités les plus puissantes de Java.

2, Android Le téléphone dispose habituellement de beaucoup de mémoire.

Donc ma réponse est NON. Je vais utiliser Enum sur Android.

4
Kai Wang

J'aime bien ajouter que vous ne pouvez pas utiliser @Annotations lorsque vous déclarez une liste <> ou une carte <> où la clé ou la valeur correspond à l'une de vos interfaces d'annotation. Vous obtenez l'erreur "Les annotations ne sont pas autorisées ici".

enum Values { One, Two, Three }
Map<String, Values> myMap;    // This works

// ... but ...
public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;

@Retention(RetentionPolicy.SOURCE)
@IntDef({ONE, TWO, THREE})
public @interface Values {}

Map<String, @Values Integer> myMap;    // *** ERROR ***

Ainsi, lorsque vous devez l'intégrer dans une liste/carte, utilisez enum, car ils peuvent être ajoutés, mais les groupes @annotated int/string ne le peuvent pas.

2
Grisgram