Android'de numaralandırmaktan kesinlikle kaçınmalı mıyım?


93

BundleAşağıdaki gibi bir arayüzde anahtarlar gibi bir dizi ilgili sabiti tanımlardım :

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

Bu bana ilgili sabitleri bir arada gruplamak ve bunları statik bir içe aktararak (uygulamalar değil) kullanmak için daha güzel bir yol sağlıyor. Bildiğim Androidçerçeve de benzer aynı şekilde sabitleri kullanır Toast.LENTH_LONG, View.GONE.

Ancak, sık sık Java Enumssabiti temsil etmenin çok daha iyi ve güçlü bir yolunu sağladığını hissediyorum .

Ancak enumson kullanmanın performans sorunu var Androidmı?

Biraz araştırmayla kafam karıştı. Android'in performans ipuçlarından "Numaralandırmalardan Kaçının" ifadesinin kaldırıldığı bu sorudan "Yalnızca İnçe İhtiyacınız Olan Sayılardan Kaçının" sorusundan , performans ipuçlarından "Numaralandırmalardan kaçının"Google kaldırıldığı açıktır , ancak resmi eğitim belgelerinden Bellek ek yükünün farkında olun, açıkça şunu söylüyor: "Numaralar genellikle statik sabitlerin iki katından fazla bellek gerektirir. Android'de numaralandırma kullanmaktan kesinlikle kaçınmalısınız. " Bu yine de geçerli mi? ( 1.6'dan sonraki sürümlerde söyleyin )Java

Ben gözlemlenen bir daha sorun göndermektir enumsgenelinde intentskullanarak Bundle(yani ben seri hale onları göndermesi gerektiğini putSerializable()pahalı bir operasyon ilkel kıyasla Bence, putString()yöntemin, satışlardaki enumsücretsiz olarak sağlamaktadır).

Biri aynı şeyi temsil etmenin en iyi yolunun hangisi olduğunu açıklayabilir Androidmi? Ben kesinlikle kullanmaktan kaçınmalısınız enumsüzerinde Android?


5
Elinizde bulunan araçları kullanmalısınız. Aslında, bir etkinlik veya parça çok fazla bellek ve işlemci kullanımı gerektirir, ancak bu, bunları kullanmayı bırakmak için bir neden değildir. Yalnızca buna ihtiyacınız varsa statik bir int kullanın ve bunlara ihtiyacınız olduğunda numaralandırmaları kullanın.
Patrick

11
Katılıyorum. Bu, erken optimizasyon gibi kokuyor. Bir performans ve / veya bellek sorununuz olmadıkça ve profil oluşturma yoluyla sorunun numaralandırmalar olduğunu kanıtlayamadığınız sürece, bunları mantıklı olduğu yerde kullanın.
GreyBeardedGeek

2
Eskiden numaralandırmaların üç değerlikli olmayan bir performans cezasına maruz kaldığına inanılırdı, ancak daha yeni kıyaslamalar bunun yerine sabitleri kullanmanın hiçbir yararı göstermez. Bkz stackoverflow.com/questions/24491160/... yanı sıra stackoverflow.com/questions/5143256/...
Benjamin Sergent

1
Bir Paketteki bir Enum'u serileştirmenin performans cezasından kaçınmak için, Enum.ordinal()bunun yerine kullanarak onu int olarak geçirebilirsiniz .
BladeCoder

1
son olarak burada
Enum'daki

Yanıtlar:


116

enumÖzelliklerine ihtiyaç duyduğunuzda kullanın . Ondan kaçınmayın kesinlikle .

Java sıralaması daha güçlüdür, ancak özelliklerine ihtiyacınız yoksa, sabitleri kullanın, daha az yer kaplarlar ve kendileri de ilkel olabilirler.

Enum ne zaman kullanılır:

  • tür denetimi - yalnızca listelenen değerleri kabul edebilirsiniz ve bunlar sürekli değildir (aşağıda sürekli dediğim şeye bakın )
  • yöntem aşırı yükleme - her enum sabiti bir yöntem için kendi uygulamasına sahiptir

    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);
    }
    
  • daha fazla veri - bir sabitiniz, tek bir değişkene konulamayan birden fazla bilgi içerir

  • karmaşık veriler - veriler üzerinde çalışmak için sürekli ihtiyaç duyduğunuz yöntemler

Ne zaman değil enum kullanmak:

  • bir türdeki tüm değerleri kabul edebilirsiniz ve sabitleriniz yalnızca en çok kullanılanları içerir
  • sürekli verileri kabul edebilirsiniz

    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);
            }
    
            ...
        }
    }
    
  • isimler için (örneğinizdeki gibi)
  • gerçekten bir numaralandırmaya ihtiyaç duymayan her şey için

Numaralandırmalar daha fazla yer kaplar

  • bir enum sabitine tek bir başvuru 4 bayt kaplar
  • her enum sabiti , alanlarının boyutlarının toplamı olan 8 bayta + nesnenin ek yüküne hizalanmış alanı kaplar
  • enum sınıfının kendisi biraz yer kaplar

Sabitler daha az yer kaplar

  • bir sabitin referansı yoktur, bu nedenle saf bir veridir (bir referans olsa bile, enum örneği başka bir referansa referans olur)
  • sabitler mevcut bir sınıfa eklenebilir - başka bir sınıf eklemek gerekli değildir
  • sabitler satır içi olabilir; genişletilmiş derleme zamanı özellikleri (boş denetim, ölü kod bulma vb.)

2
İnt sabitleri için tür denetimini simüle etmek için @IntDef açıklamasını kullanabilirsiniz. Java Enums lehine bir argüman daha az.
BladeCoder

3
@BladeCoder hayır, sabiti referans almanıza gerek yok
Kamil Jarosz

1
Ayrıca, enums kullanmanın yansıma kullanımını teşvik ettiğini unutmamakta fayda var. Ve yansıma kullanımının Android için BÜYÜK bir performans artışı olduğu biliniyor. Buraya bakın: blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark

3
@ w3bshark Numaralandırmalar yansıma kullanımını nasıl teşvik eder?
Kevin Krumwiede

3
Akılda tutulması gereken bir diğer nokta da, standart boş "Merhaba dünya!" Dan bir yığın dökümüdür. proje 4.000'den fazla sınıf ve 700.000 nesne içerir. Binlerce sabiti olan gülünç derecede büyük bir numaranız olsa bile, performans etkisinin Android çerçevesinin kendisinin şişkinliğinin yanında ihmal edilebilir olacağından emin olabilirsiniz.
Kevin Krumwiede

58

Numaralandırmalar yalnızca değerlere sahipse, burada gösterildiği gibi IntDef / StringDef'i kullanmayı denemelisiniz:

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

Örnek: yerine:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

kullan:

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

ve bunu parametre / döndürülen değer olarak içeren işlevde şunu kullanın:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

Numaralandırmanın karmaşık olması durumunda, bir enum kullanın. O kadar da kötü değil.

Numaralandırmaları sabit değerlerle karşılaştırmak için burayı okumalısınız:

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

Örnekleri 2 değerli bir numaralandırmadır. Sabit tamsayılar kullanıldığında 128 bayta kıyasla dex dosyasında 1112 bayt alır. C / C ++ üzerinde nasıl çalıştığının aksine numaralandırmalar gerçek sınıflar olduğundan mantıklıdır.


Numaralandırmanın artılarını / eksilerini sağlayan diğer yanıtları takdir etsem de, özellikle Android için en iyi çözümü sağladığı için bu yanıt kabul edilen yanıt olmalıdır. Destek ek açıklamaları gitmenin yoludur. Android geliştiricileri olarak, daha sonra bellek / performans konusunda endişelenmeye başlamazsam, diğer yanıtların düşünme tarzı yerine "numaralandırmak için güçlü bir nedenim yoksa, sabitleri ek açıklamalarla kullanacağım" diye düşünmemiz gerekir. Numaralandırmayı kullanabilirim ". Kendinizi daha sonra performans problemleri yaşamaktan şimdi durdurun!
w3bshark

3
@ w3bshark Performans sorunları yaşıyorsanız, bunları çözmek için düşünmeniz gereken ilk şeyin numaralandırma olması pek olası değildir. Ek açıklamaların kendi değiş tokuşları vardır: derleyici desteğine sahip değiller, kendi alanlarına ve yöntemlerine sahip değiller, yazmak zahmetli, bir int ek açıklama eklemeyi kolayca unutabilirsiniz, vb. Genel olarak daha iyi bir çözüm değiller, ihtiyacınız olduğunda yığın alanından tasarruf etmek için oradalar.
Malcolm

1
@androiddeveloper Enum bildiriminde tanımlanmamış bir sabiti geçerek hata yapamazsınız. Bu kod asla derlenmeyecek. Bir IntDefaçıklama ile yanlış bir sabiti geçerseniz , o olacaktır. Saatlere gelince, bence oldukça açık. Hangi cihazda daha fazla CPU gücü, RAM ve pil var: telefon mu yoksa saat mi? Ve bundan dolayı hangisinin performans için daha optimize edilmiş bir yazılıma ihtiyacı var?
Malcolm

3
@androiddeveloper Tamam, katı terminolojide ısrar ediyorsanız, hatalardan derleme zamanı hatası olmayan hataları kastetmiştim (çalışma zamanı veya program mantığı hataları). Beni mi kandırıyorsun yoksa bunlar ile derleme zamanı hatalarının faydaları arasındaki farkı gerçekten anlamıyor musun? Bu iyi tartışılmış bir konudur, buna internette bakın. Saatlerle ilgili olarak, Android daha fazla cihaz içerir, ancak en kısıtlı olanlar en düşük ortak payda olacaktır.
Malcolm

2
@androiddeveloper Bu iki ifadeye de zaten yanıt verdim, bir kez daha tekrarlamak söylediklerimi geçersiz kılmaz.
Malcolm

12

Önceki cevaplara ek olarak, eğer Proguard kullanıyorsanız (ve boyutu küçültmek ve kodunuzu gizlemek için kesinlikle yapmalısınız), o Enumszaman otomatik olarak@IntDef olarak mümkün her yere :

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

sınıf / kutudan çıkarma / numaralandırma

Mümkün olduğunda, enum türlerini tamsayı sabitlerine basitleştirir.

Bu nedenle, bazı ayrık değerleriniz varsa ve bazı yöntemler aynı türden diğerlerinin değil, yalnızca bu değerleri almanıza izin vermelidir, o zaman kullanırım Enum , çünkü Proguard bu kılavuzu benim için kodu optimize etme işini yapacak.

Ve işte Jake Wharton'daki numaralandırmaları kullanma hakkında güzel bir gönderi, bir göz atın.

Bir kütüphane geliştiricisi olarak, tüketen uygulamanın boyutu, belleği ve performansı üzerinde mümkün olduğunca az etkiye sahip olmak istediğimiz için yapılması gereken bu küçük optimizasyonların farkındayım. Ancak, [...] genel API'nizde uygun olan yerlerde tamsayı değerlerine bir enum koymanın tamamen iyi olduğunu anlamak önemlidir. Önemli olan bilinçli kararlar vermenin farkını bilmek


11

Android'de numaralandırmaktan kesinlikle kaçınmalı mıyım?

Hayır. " Kesinlikle " çok kötü oldukları anlamına gelir, hiç kullanılmamaları gerekir. Muhtemelen , numaralandırmalı (ui iş parçacığında ardışık) birçok (binlerce veya milyonlarca) işlem gibi uç bir durumda bir performans sorunları ortaya çıkabilir . Çok daha yaygın olanı, kesinlikle bir arka plan iş parçacığında gerçekleşmesi gereken ağ G / Ç işlemleri . Çeteleler en yaygın kullanımı muhtemelen tip onay çeşit - bir nesne olup olmadığını bu ya o kadar hızlı sen çeteleler tek karşılaştırılması ve tamsayılar bir karşılaştırma arasında bir fark görebilir etmek mümkün olmayacaktır hangi.

Birisi Android'de aynı şeyi temsil etmenin en iyi yolunun hangisi olduğunu açıklayabilir mi?

Bunun için genel bir kural yok. Sizin için uygun olan ve uygulamanızı hazırlamanıza yardımcı olan her şeyi kullanın. Daha sonra optimize edin - uygulamanızın bazı yönlerini yavaşlatan bir darboğaz olduğunu fark ettikten sonra.


1
Sabitleri destek bilgi notlarıyla kullanmak ve şimdi optimize etmek kadar kolayken neden daha sonra optimize edesiniz? @ Android_developer'in cevabına buradan bakın.
w3bshark


4

İki gerçek.

1, Enum, JAVA'daki en güçlü özelliklerden biridir.

2, Android telefon genellikle çok fazla belleğe sahiptir.

Yani cevabım HAYIR. Enum'u Android'de kullanacağım.


2
Android'in çok fazla belleği varsa, bu, uygulamanızın hepsini tüketmesi ve başkaları için hiçbir şey bırakmaması gerektiği anlamına gelmez. Uygulamanızı Android işletim sistemi tarafından öldürülmekten kurtarmak için en iyi uygulamaları takip etmelisiniz. Numaralandırma kullanmayın demiyorum, sadece başka alternatifiniz olmadığında kullanın.
Varundroid

1
En iyi uygulamayı takip etmemiz gerektiği konusunda size katılıyorum, ancak en iyi uygulama her zaman en az bellek kullanmak anlamına gelmez. Kodu temiz tutmak, anlaşılması kolay, birkaç k bellek kaydetmekten çok daha önemlidir. Bir denge bulmalısın.
Kai Wang

1
Bir denge bulmamız gerektiği konusunda size katılıyorum, ancak TypeDef'i kullanmak kodumuzu kötü veya daha az bakım yapılabilir yapmaz. Bunu bir yıldan fazla bir süredir kullanıyorum ve kodumun anlaşılmasının kolay olmadığını hiç hissetmedim, ayrıca meslektaşlarımın hiçbiri TypeDefs'in numaralandırmalara kıyasla anlaşılmasının zor olduğundan şikayet etmedi. Benim tavsiyem, numaralandırmaları yalnızca başka seçeneğiniz olmadığında kullanmanız, ancak mümkünse kaçınmanızdır.
Varundroid

2

Eklemek isterim ki, bir Liste <> veya Harita <> ilan ettiğinizde @Annotations'ı kullanamazsınız, burada anahtar veya değer, açıklama arayüzlerinizden biridir. "Burada ek açıklamalara izin verilmiyor" hatasını alıyorsunuz.

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

Bu nedenle, onu bir liste / haritaya paketlemeniz gerektiğinde, eklenebilecekleri için enum kullanın, ancak @ açıklamalı int / string grupları olamaz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.