Devo assolutamente evitare di usare le enumerazioni su Android?


92

Definivo insieme un insieme di costanti correlate come le Bundlechiavi in ​​un'interfaccia come di seguito:

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

Questo mi fornisce un modo migliore per raggruppare le costanti correlate e usarle facendo un'importazione statica (non implementa). So Androidquadro utilizza anche le costanti nello stesso modo come Toast.LENTH_LONG, View.GONE.

Tuttavia, sento spesso che il file Java Enums forniscano un modo molto migliore e potente per rappresentare la costante.

Ma c'è un problema di prestazioni nell'uso enumsdiAndroid ?

Con un po 'di ricerca sono finito in confusione. Da questa domanda "Evita enumerazioni dove hai solo bisogno di Ints" rimossa dai suggerimenti sulle prestazioni di Android? È chiaro che Googleha rimosso "Evita enumerazioni" dai suoi suggerimenti sulle prestazioni, ma dai suoi documenti di formazione ufficiali Fai attenzione alla sezione relativa al sovraccarico della memoria che dice chiaramente: " spesso richiedono più del doppio della memoria rispetto alle costanti statiche. Dovresti assolutamente evitare di utilizzare le enumerazioni su Android. " È ancora valido? (Ad esempio nelle Javaversioni successive alla 1.6)

Un problema più che ho osservato è quello di inviare enumstutto intentstramite Bundledovrei inviarli via serializzazione (vale a dire putSerializable(), che ritengo un'operazione costosa rispetto al primitivo putString()metodo, eventhoughenums fornisce gratuitamente).

Qualcuno può chiarire qual è il modo migliore per rappresentare lo stesso in Android? Devo assolutamente evitare di usare enumsil Android?


5
Dovresti usare gli strumenti che hai a disposizione. In effetti, un'attività o un frammento occupa molta memoria e utilizzo della CPU, ma non è un motivo per smettere di usarli. Usa un int statico se ne hai solo bisogno e usa le enumerazioni quando ne hai bisogno.
Patrick

11
Sono d'accordo. Questo puzza di ottimizzazione prematura. A meno che tu non abbia un problema di prestazioni e / o memoria e possa dimostrare attraverso la profilazione che le enumerazioni sono la causa, usale dove ha senso.
GreyBeardedGeek

2
Si credeva che gli enum subissero una penalizzazione delle prestazioni non trival, ma i benchmark più recenti non mostrano alcun vantaggio nell'usare invece le costanti. Vedi stackoverflow.com/questions/24491160/… e stackoverflow.com/questions/5143256/…
Benjamin Sergent

1
Per evitare la penalizzazione delle prestazioni della serializzazione di un Enum in un bundle, è possibile passarlo come int utilizzando Enum.ordinal()invece.
BladeCoder

1
finalmente ci sono alcune spiegazioni sui problemi di prestazioni su Enum qui youtube.com/watch?v=Hzs6OBcvNQE
nvinayshetty

Risposte:


115

Usalo enumquando ne hai bisogno. Non evitarlo rigorosamente .

Java enum è più potente, ma se non hai bisogno delle sue funzionalità, usa le costanti, occupano meno spazio e possono essere primitive.

Quando usare enum:

  • controllo del tipo: puoi accettare solo i valori elencati e non sono continui (vedi sotto quello che chiamo continuo qui)
  • sovraccarico del metodo: ogni costante enum ha la propria implementazione di un metodo

    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);
    }
  • più dati: la tua unica costante contiene più di un'informazione che non può essere inserita in una variabile

  • dati complicati: i tuoi metodi di necessità costante per operare sui dati

Quando non usare enum:

  • puoi accettare tutti i valori di un tipo e le tue costanti contengono solo quelli più utilizzati
  • puoi accettare dati continui

    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);
            }
    
            ...
        }
    }
  • per i nomi (come nel tuo esempio)
  • per tutto il resto che in realtà non necessita di enumerazione

Gli enum occupano più spazio

  • un singolo riferimento a una costante enum occupa 4 byte
  • ogni costante enum occupa uno spazio che è una somma delle dimensioni dei suoi campi allineati a 8 byte + overhead dell'oggetto
  • la classe enum stessa occupa un po 'di spazio

Le costanti occupano meno spazio

  • una costante non ha un riferimento quindi è un dato puro (anche se è un riferimento, l'istanza enum sarebbe un riferimento a un altro riferimento)
  • le costanti possono essere aggiunte a una classe esistente - non è necessario aggiungere un'altra classe
  • le costanti possono essere inline; offre funzionalità estese in fase di compilazione (come il controllo null, la ricerca di codice morto ecc.)

2
È possibile utilizzare l'annotazione @IntDef per simulare il controllo del tipo per le costanti int. È un argomento in meno a favore di Java Enums.
BladeCoder

3
@BladeCoder no, non hai bisogno di un riferimento alla costante
Kamil Jarosz

1
Inoltre, è bene notare che l'uso di enumerazioni promuove l'uso della riflessione. Ed è noto che l'utilizzo della riflessione è un enorme successo in termini di prestazioni per Android. Vedi qui: blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark

3
@ w3bshark In che modo le enumerazioni promuovono l'uso della riflessione?
Kevin Krumwiede

3
Un'altra cosa da tenere a mente è che un heap dump dallo standard vuoto "Hello, world!" progetto comprende oltre 4.000 classi e 700.000 oggetti. Anche se avessi un enum ridicolmente enorme con migliaia di costanti, puoi essere certo che l'effetto sulle prestazioni sarebbe trascurabile accanto al gonfio del framework Android stesso.
Kevin Krumwiede

57

Se le enumerazioni hanno semplicemente valori, dovresti provare a utilizzare IntDef / StringDef, come mostrato qui:

https://developer.android.com/studio/write/annotations.html#enum-annotations

Esempio: invece di:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

usate:

@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;

e nella funzione che lo ha come parametro / valore restituito, usa:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

Nel caso in cui l'enum sia complesso, usa un enum. Non è così male.

Per confrontare enumerazioni con valori costanti, dovresti leggere qui:

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

Il loro esempio è di un'enumerazione con 2 valori. Richiede 1112 byte nel file dex rispetto ai 128 byte quando vengono utilizzati numeri interi costanti. Ha senso, poiché le enumerazioni sono classi reali, al contrario di come funziona in C / C ++.


Sebbene apprezzi le altre risposte che forniscono pro / contro delle enumerazioni, questa risposta dovrebbe essere la risposta accettata poiché fornisce la migliore soluzione per Android, in particolare. Le annotazioni di supporto sono la strada da percorrere. Noi, come sviluppatori Android dovremmo pensare "a meno che non abbia un valido motivo per usare le enumerazioni, userò le costanti con le annotazioni", piuttosto che il modo di pensare delle altre risposte "a meno che non inizi a preoccuparmi della memoria / delle prestazioni in seguito, Posso usare le enumerazioni ". Smettila ora di avere problemi di prestazioni in seguito!
w3bshark

3
@ w3bshark Se hai problemi di prestazioni, è improbabile che le enumerazioni siano la prima cosa a cui dovresti pensare per risolverli. Le annotazioni hanno i loro compromessi: non hanno il supporto del compilatore, non possono avere i propri campi e metodi, sono noiose da scrivere, puoi facilmente dimenticare di annotare un int e così via. Non sono una soluzione migliore in generale, sono lì solo per risparmiare spazio quando è necessario.
Malcolm

1
@androiddeveloper Non puoi commettere errori passando una costante non definita nella dichiarazione enum. Questo codice non verrà mai compilato. Se passi una costante sbagliata con IntDefun'annotazione, lo farà. Per quanto riguarda gli orologi, penso che sia abbastanza chiaro anche. Quale dispositivo ha più potenza della CPU, RAM e batteria: un telefono o un orologio? E quali richiedono che il software sia più ottimizzato per le prestazioni a causa di ciò?
Malcolm

3
@androiddeveloper OK, se insisti su una terminologia rigorosa, per errori intendo errori che non sono errori in fase di compilazione (errori di runtime o di logica del programma). Mi stai trollando o davvero non capisci la differenza tra loro ei vantaggi degli errori in fase di compilazione? Questo è un argomento ben discusso, cercalo sul web. Per quanto riguarda gli orologi, Android include più dispositivi, ma quelli più vincolati saranno il minimo comune denominatore.
Malcolm

2
@androiddeveloper Ho già risposto a entrambe le affermazioni, ripeterle ancora una volta non invalida ciò che ho detto.
Malcolm

12

Oltre alle risposte precedenti, aggiungerei che se stai usando Proguard (e dovresti assolutamente farlo per ridurre le dimensioni e offuscare il tuo codice), il tuo Enumsverrà automaticamente convertito in@IntDef ovunque sia possibile:

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

class / unboxing / enum

Semplifica i tipi enum in costanti intere, quando possibile.

Pertanto, se hai dei valori discreti e qualche metodo dovrebbe consentire di prendere solo questi valori e non altri dello stesso tipo, allora userei Enum userei, perché Proguard farà questo lavoro manuale di ottimizzazione del codice per me.

Ed ecco un buon post sull'uso delle enumerazioni di Jake Wharton, dai un'occhiata.

In qualità di sviluppatore di librerie, riconosco queste piccole ottimizzazioni che dovrebbero essere fatte poiché vogliamo avere il minor impatto possibile sulle dimensioni, la memoria e le prestazioni dell'app che consumano. Ma è importante rendersi conto che mettere [...] un enum nella tua API pubblica rispetto a valori interi dove appropriato va perfettamente bene. Sapere la differenza per prendere decisioni informate è ciò che è importante


11

Devo assolutamente evitare di usare le enumerazioni su Android?

No. " Rigorosamente " significa che sono così cattivi, non dovrebbero essere usati affatto. È possibile che si verifichino problemi di prestazioni in una situazione estrema come molte molte (migliaia o milioni di) operazioni con enumerazioni (consecutive sul thread dell'interfaccia utente). Molto più comuni sono le operazioni di I / O di rete che dovrebbero avvenire rigorosamente in un thread in background. L'utilizzo più comune di enumerazioni è probabilmente una sorta di controllo del tipo: se un oggetto è questo o quello che è così veloce non sarai in grado di notare una differenza tra un singolo confronto di enumerazioni e un confronto di interi.

Qualcuno può chiarire qual è il modo migliore per rappresentare lo stesso in Android?

Non esiste una regola generale per questo. Usa quello che funziona per te e ti aiuta a preparare la tua app. Ottimizza in seguito: dopo aver notato che c'è un collo di bottiglia che rallenta alcuni aspetti della tua app.


1
Perché dovresti ottimizzare in seguito, quando è altrettanto facile usare le costanti con le annotazioni di supporto e ottimizzare ora? Vedi la risposta di @ android_developer qui.
w3bshark


4

Due fatti.

1, Enum è una delle funzionalità più potenti di JAVA.

2, il telefono Android di solito ha MOLTA memoria.

Quindi la mia risposta è NO. Userò Enum su Android.


2
Se Android ha MOLTA memoria, ciò non significa che la tua app debba consumarla tutta e non lasciarla agli altri. Dovresti seguire le migliori pratiche per evitare che la tua app venga uccisa dal sistema operativo Android. Non sto dicendo di non usare enumerazioni, ma di usarle solo quando non hai alternative.
Varundroid

1
Sono d'accordo con te sul fatto che dovremmo seguire la migliore pratica, tuttavia, la migliore pratica non sempre significa usare meno memoria. Mantenere il codice pulito e di facile comprensione è molto più importante che salvare diversi k di memoria. Devi trovare un equilibrio.
Kai Wang

1
Sono d'accordo con te sul fatto che dobbiamo trovare un equilibrio, ma l'uso di TypeDef non renderà il nostro codice cattivo o meno manutenibile. Lo sto usando da più di un anno e non ho mai pensato che il mio codice non fosse facile da capire, inoltre nessuno dei miei colleghi si è mai lamentato del fatto che i TypeDef siano difficili da capire rispetto alle enumerazioni. Il mio consiglio è di usare le enumerazioni solo quando non hai altra opzione, ma evitala se puoi.
Varundroid

2

Mi piace aggiungere che non puoi usare @Annotations quando dichiari un List <> o Map <> dove la chiave o il valore è di una delle tue interfacce di annotazione. Viene visualizzato l'errore "Le annotazioni non sono consentite qui".

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 ***

Quindi, quando è necessario comprimerlo in un elenco / mappa, utilizzare enum, poiché possono essere aggiunti, ma i gruppi int / string @annotated non possono.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.