Devo evitar estritamente o uso de enums no Android?

92

Eu costumava definir um conjunto de constantes relacionadas como Bundlechaves juntas em uma interface como abaixo:

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

Isso me fornece uma maneira mais agradável de agrupar constantes relacionadas e usá-las fazendo uma importação estática (não implementos). Eu sei que o Androidframework também usa as constantes da mesma maneira Toast.LENTH_LONG, como View.GONE,.

No entanto, muitas vezes sinto que Java Enumsfornecem uma maneira muito melhor e poderosa de representar a constante.

Mas há um problema de desempenho no uso do enumson Android?

Com um pouco de pesquisa, acabei ficando confuso. Desta questão "Avoid Enums Onde Você Só Precisa de Ints" removida das dicas de desempenho do Android? Está claro que Googleremoveu "Avoid enums" de suas dicas de desempenho, mas de seus documentos oficiais de treinamento. Esteja ciente da seção de sobrecarga de memória diz claramente: "Enums freqüentemente requerem mais do que o dobro de memória que constantes estáticas. Você deve evitar estritamente o uso de enums no Android. " Isso ainda é válido? (Digamos nas Javaversões posteriores à 1.6)

Mais uma questão que eu observei é enviar enumsatravés intentsusando Bundledevo enviá-los por serialização (ou seja putSerializable(), que eu acho que uma operação cara em comparação com primitivo putString()método, eventhoughenums fornece-lo gratuitamente).

Alguém pode esclarecer qual é a melhor maneira de representar o mesmo em Android? Devo estritamente evitar o uso enumsde Android?

nvinayshetty
fonte
5
Você deve usar as ferramentas que tem disponíveis. Na verdade, uma atividade ou fragmento consome muita memória e uso de CPU, mas não há razão para parar de usá-los. Use um int estático se precisar apenas dele e use enums quando precisar deles.
Patrick
11
Concordo. Isso cheira a otimização prematura. A menos que você esteja tendo um problema de desempenho e / ou memória e possa provar por meio da criação de perfil que enums são a causa, use-os onde fizer sentido.
GreyBeardedGeek
2
Costumava-se acreditar que os enums incorriam em uma penalidade de desempenho não triplo, mas os benchmarks mais recentes não mostram nenhum benefício em usar constantes. Consulte stackoverflow.com/questions/24491160/… bem como stackoverflow.com/questions/5143256/…
Benjamin Sergent
1
Para evitar a penalidade de desempenho de serializar um Enum em um Bundle, você pode passá-lo como um int usando Enum.ordinal().
BladeCoder
1
finalmente, há algumas explicações sobre problemas de desempenho no Enum aqui youtube.com/watch?v=Hzs6OBcvNQE
nvinayshetty

Respostas:

115

Use enumquando precisar de seus recursos. Não o evite estritamente .

Java enum é mais poderoso, mas se você não precisa de seus recursos, use constantes, elas ocupam menos espaço e podem ser primitivas.

Quando usar enum:

  • verificação de tipo - você pode aceitar apenas os valores listados, e eles não são contínuos (veja abaixo o que chamo de contínuo aqui)
  • sobrecarga de método - cada constante de enum tem sua própria implementação de um método

    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);
    }
  • mais dados - sua única constante contém mais de uma informação que não pode ser colocada em uma variável

  • dados complicados - seus métodos de necessidade constante para operar nos dados

Quando não usar enum:

  • você pode aceitar todos os valores de um tipo, e suas constantes contêm apenas os mais usados
  • você pode aceitar dados contínuos

    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);
            }
    
            ...
        }
    }
  • para nomes (como no seu exemplo)
  • para tudo o mais que realmente não precisa de um enum

Enums ocupam mais espaço

  • uma única referência a uma constante enum ocupa 4 bytes
  • cada constante enum ocupa espaço que é uma soma dos tamanhos de seus campos alinhados a 8 bytes + sobrecarga do objeto
  • a própria classe enum ocupa algum espaço

Constantes ocupam menos espaço

  • uma constante não tem uma referência, então é um dado puro (mesmo se for uma referência, a instância de enum seria uma referência para outra referência)
  • constantes podem ser adicionadas a uma classe existente - não é necessário adicionar outra classe
  • constantes podem ser inline; ele traz recursos de tempo de compilação estendidos (como verificação de nulos, localização de código morto etc.)
Kamil Jarosz
fonte
2
Você pode usar a anotação @IntDef para simular a verificação de tipo para constantes int. É um argumento a menos a favor do Java Enums.
BladeCoder
3
@BladeCoder não, você não precisa de uma referência para constante
Kamil Jarosz
1
Além disso, é bom observar que o uso de enums promove o uso de reflexão. E é sabido que o uso de reflexão é um grande golpe de desempenho para Android. Veja aqui: blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark
3
@ w3bshark Como os enums promovem o uso de reflexão?
Kevin Krumwiede
3
Outra coisa a ter em mente é que um despejo de heap do vazio padrão "Olá, mundo!" projeto inclui mais de 4.000 classes e 700.000 objetos. Mesmo se você tivesse um enum absurdamente grande com milhares de constantes, pode ter certeza de que o efeito de desempenho seria insignificante próximo ao inchaço da própria estrutura do Android.
Kevin Krumwiede
57

Se os enums simplesmente tiverem valores, você deve tentar usar IntDef / StringDef, conforme mostrado aqui:

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

Exemplo: em vez de:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

você usa:

@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 na função que o possui como parâmetro / valor retornado, use:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

Caso o enum seja complexo, use um enum. Não é tão ruim.

Para comparar enums e valores constantes, você deve ler aqui:

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

O exemplo deles é um enum com 2 valores. Leva 1112 bytes no arquivo dex em comparação com 128 bytes quando inteiros constantes são usados. Faz sentido, já que enums são classes reais, ao contrário de como funcionam em C / C ++.

desenvolvedor android
fonte
Embora eu aprecie as outras respostas que fornecem prós / contras de enums, essa resposta deve ser aceita, pois fornece a melhor solução para Android, especificamente. As anotações de suporte são o caminho a percorrer. Nós, como desenvolvedores Android, devemos pensar "a menos que eu tenha um forte motivo para usar enums, usarei constantes com anotações", em vez da maneira de pensar das outras respostas "a menos que eu comece a me preocupar com memória / desempenho mais tarde, Eu posso usar enums ". Pare de ter problemas de desempenho mais tarde!
w3bshark
3
@ w3bshark Se você está tendo problemas de desempenho, é improvável que enums sejam a primeira coisa em que você deve pensar para resolvê-los. As anotações têm suas próprias compensações: não têm suporte de compilador, não podem ter seus próprios campos e métodos, são tediosas de escrever, você pode facilmente esquecer de anotar um int e assim por diante. Eles não são uma solução melhor em geral, eles estão lá apenas para economizar espaço de heap quando você precisar.
Malcolm
1
@androiddeveloper Você não pode cometer um erro ao passar uma constante não definida na declaração enum. Este código nunca será compilado. Se você passar uma constante errada com uma IntDefanotação, isso acontecerá. Quanto aos relógios, também acho bem claro. Qual dispositivo tem mais CPU, RAM e bateria: um telefone ou um relógio? E quais precisam de software para ser mais otimizado para desempenho por causa disso?
Malcolm
3
@androiddeveloper OK, se você insiste na terminologia estrita, por erros eu quis dizer erros que não são erros de tempo de compilação (erros de tempo de execução ou de lógica de programa). Você está me enganando ou realmente não entende a diferença entre eles e os benefícios dos erros de tempo de compilação? Este é um assunto bem discutido, pesquise na web. Em relação aos relógios, o Android inclui mais dispositivos, mas os mais restritos serão o menor denominador comum.
Malcolm
2
@androiddeveloper Eu já respondi a ambas as declarações, repeti-las mais uma vez não invalida o que eu disse.
Malcolm
12

Além das respostas anteriores, eu acrescentaria que se você estiver usando o Proguard (e você definitivamente deve fazer isso para reduzir o tamanho e ofuscar seu código), então seu Enumsserá automaticamente convertido para @IntDefonde for possível:

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

classe / unboxing / enum

Simplifica os tipos de enum para constantes inteiras, sempre que possível.

Portanto, se você tem alguns valores discretos e algum método deve permitir pegar apenas esses valores e não outros do mesmo tipo, eu usaria Enum , pois o Proguard fará esse trabalho manual de otimização de código para mim.

E aqui está um bom post sobre o uso de enums de Jake Wharton, dê uma olhada nele.

Como um desenvolvedor de biblioteca, reconheço essas pequenas otimizações que devem ser feitas, pois queremos ter o mínimo impacto possível sobre o tamanho, a memória e o desempenho do aplicativo de consumo. Mas é importante perceber que colocar um [...] enum em sua API pública versus valores inteiros onde apropriado é perfeitamente adequado. Saber a diferença para tomar decisões informadas é o que importa

Gaket
fonte
11

Devo evitar estritamente o uso de enums no Android?

Não. " Estritamente " significa que eles são tão ruins que não devem ser usados. Possivelmente, um problema de desempenho pode surgir em uma situação extrema, como muitas, muitas (milhares ou milhões de) operações com enums (consecutivos no encadeamento da interface do usuário). Muito mais comuns são as operações de E / S de rede que devem acontecer estritamente em um thread de segundo plano. O uso mais comum de enums é provavelmente algum tipo de verificação de tipo - se um objeto é este ou aquele que é tão rápido que você não será capaz de notar a diferença entre uma única comparação de enums e uma comparação de inteiros.

Alguém pode esclarecer qual é a melhor maneira de representar o mesmo no Android?

Não existe uma regra geral para isso. Use o que funciona para você e ajuda a preparar seu aplicativo. Otimize mais tarde - depois de notar que há um gargalo que retarda alguns aspectos do seu aplicativo.

stan0
fonte
1
Por que você otimizaria mais tarde, quando é tão fácil usar constantes com anotações de suporte e otimizar agora? Veja a resposta de @ android_developer aqui.
w3bshark
4

Dois fatos.

1, Enum é um dos recursos mais poderosos em JAVA.

2, o telefone Android geralmente tem MUITA memória.

Portanto, minha resposta é NÃO. Vou usar Enum no Android.

Kai Wang
fonte
2
Se o Android tem MUITA memória, isso não significa que seu aplicativo deva consumir tudo e não deixar nada para os outros. Você deve seguir as práticas recomendadas para evitar que seu aplicativo seja interrompido pelo sistema operacional Android. Não estou dizendo para não usar enums, mas use apenas quando não tiver alternativa.
Varundroid
1
Concordo com você que devemos seguir as melhores práticas, no entanto, a melhor prática nem sempre significa usar menos memória. Manter o código limpo e fácil de entender é muito mais importante do que salvar vários k de memória. Você precisa encontrar um equilíbrio.
Kai Wang
1
Eu concordo com você que precisamos encontrar um equilíbrio, mas usar TypeDef não fará com que nosso código pareça ruim ou menos sustentável. Estou usando há mais de um ano e nunca senti que meu código não fosse fácil de entender, também nenhum dos meus colegas jamais reclamou que TypeDefs são difíceis de entender em comparação com enums. Meu conselho é usar enums apenas quando você não tiver outra opção a não ser evitá-lo, se puder.
Varundroid
2

Gosto de acrescentar que você não pode usar @Annotations ao declarar uma Lista <> ou Mapa <> onde a chave ou o valor é de uma de suas interfaces de anotação. Você obtém o erro "Anotações não são permitidas aqui".

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

Portanto, quando você precisar empacotar em uma lista / mapa, use enum, pois eles podem ser adicionados, mas os grupos @annotated int / string não podem.

Grisgram
fonte