Haruskah saya benar-benar menghindari penggunaan enum di Android?

93

Saya biasa mendefinisikan satu set konstanta terkait seperti Bundlekunci bersama-sama dalam antarmuka seperti di bawah ini:

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

Ini memberi saya cara yang lebih baik untuk mengelompokkan konstanta terkait dan menggunakannya dengan membuat impor statis (bukan mengimplementasikan). Saya tahu Androidkerangka juga menggunakan konstanta dengan cara yang sama seperti Toast.LENTH_LONG, View.GONE.

Namun, saya sering merasa bahwa cara Java Enumsmemberikan jauh lebih baik dan ampuh untuk mewakili konstanta.

Tapi apakah ada masalah performence dalam menggunakan enumson Android?

Dengan sedikit riset, saya berakhir dalam kebingungan. Dari pertanyaan ini ? "Enum Hindari Dimana Anda Hanya Perlu Ints” dihapus dari tips kinerja Android jelas bahwa Googletelah dihapus 'Hindari enum' dari tips kinerjanya, tapi dari itu docs pelatihan resmi Menyadari overhead memori bagian itu jelas mengatakan: "Enums seringkali membutuhkan lebih dari dua kali lebih banyak memori sebagai konstanta statis. Anda harus benar-benar menghindari penggunaan enum di Android. " Apakah ini masih berlaku? (Katakanlah dalam Javaversi setelah 1.6)

Salah satu isu lagi yang saya amati adalah untuk mengirim enumsseluruh intentsmenggunakan Bundlesaya harus mengirim mereka dengan serialisasi (yaitu putSerializable(), yang saya pikir operasi yang mahal dibandingkan dengan primitif putString()metode, walaupun enumsmenyediakan secara gratis).

Bisakah seseorang menjelaskan cara terbaik untuk mewakili hal yang sama Android? Haruskah saya benar-benar menghindari penggunaan enumson Android?

nvinayshetty
sumber
5
Anda harus menggunakan alat yang Anda miliki. Faktanya, suatu aktivitas atau fragmen membutuhkan banyak memori dan penggunaan cpu, tetapi itu bukan alasan untuk berhenti menggunakannya. Gunakan int statis jika Anda hanya membutuhkannya, dan gunakan enum saat Anda membutuhkannya.
Patrick
11
Saya setuju. Ini berbau seperti pengoptimalan prematur. Kecuali jika Anda mengalami masalah performa dan / atau memori, dan dapat membuktikan melalui pembuatan profil bahwa enum adalah penyebabnya, gunakan enum di tempat yang masuk akal.
GreyBeardedGeek
2
Dulu diyakini bahwa enum menghasilkan penalti kinerja non-trival, tetapi tolok ukur yang lebih baru tidak menunjukkan manfaat menggunakan konstanta. Lihat stackoverflow.com/questions/24491160/… serta stackoverflow.com/questions/5143256/…
Benjamin Sergent
1
Untuk menghindari penalti kinerja serialisasi Enum dalam Bundel, Anda dapat meneruskannya sebagai int menggunakan Enum.ordinal().
BladeCoder
1
akhirnya ada beberapa penjelasan tentang masalah kinerja pada Enum di sini youtube.com/watch?v=Hzs6OBcvNQE
nvinayshetty

Jawaban:

116

Gunakan enumsaat Anda membutuhkan fitur-fiturnya. Jangan menghindarinya dengan ketat .

Java enum lebih kuat, tetapi jika Anda tidak membutuhkan fiturnya, gunakan konstanta, mereka menempati lebih sedikit ruang dan mereka bisa menjadi primitif itu sendiri.

Kapan menggunakan enum:

  • jenis pemeriksaan - Anda hanya dapat menerima nilai yang terdaftar, dan tidak kontinu (lihat di bawah apa yang saya sebut kontinu di sini)
  • method overloading - setiap konstanta enum memiliki implementasi metode sendiri-sendiri

    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);
    }
    
  • lebih banyak data - satu konstanta Anda berisi lebih dari satu informasi yang tidak dapat dimasukkan ke dalam satu variabel

  • data rumit - metode kebutuhan konstan Anda untuk mengoperasikan data

Kapan tidak menggunakan enum:

  • Anda dapat menerima semua nilai dari satu jenis, dan konstanta Anda hanya berisi yang paling sering digunakan
  • Anda dapat menerima data berkelanjutan

    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);
            }
    
            ...
        }
    }
    
  • untuk nama (seperti dalam contoh Anda)
  • untuk semua hal lain yang benar-benar tidak membutuhkan enum

Enum menempati lebih banyak ruang

  • referensi tunggal ke konstanta enum menempati 4 byte
  • setiap konstanta enum menempati ruang yang merupakan jumlah dari ukuran bidangnya selaras dengan 8 byte + overhead objek
  • kelas enum itu sendiri menempati beberapa ruang

Konstanta menempati lebih sedikit ruang

  • sebuah konstanta tidak memiliki referensi jadi ini adalah data murni (meskipun itu referensi, maka instance enum akan menjadi referensi ke referensi lain)
  • konstanta dapat ditambahkan ke kelas yang sudah ada - tidak perlu menambahkan kelas lain
  • konstanta mungkin sebaris; itu membawa fitur waktu kompilasi yang diperpanjang (seperti pemeriksaan nol, menemukan kode mati, dll.)
Kamil Jarosz
sumber
2
Anda dapat menggunakan anotasi @IntDef untuk menyimulasikan pemeriksaan jenis untuk konstanta int. Itu satu argumen yang mendukung Java Enums.
BladeCoder
3
@BladeCoder tidak, Anda tidak memerlukan referensi ke konstanta
Kamil Jarosz
1
Juga, ada baiknya untuk dicatat bahwa menggunakan enum meningkatkan penggunaan refleksi. Dan diketahui bahwa penggunaan refleksi adalah performa yang BESAR untuk Android. Lihat di sini: blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark
3
@ w3bshark Bagaimana enum mempromosikan penggunaan refleksi?
Kevin Krumwiede
3
Hal lain yang perlu diingat adalah bahwa heap dump dari standar kosong "Halo, dunia!" proyek mencakup lebih dari 4.000 kelas dan 700.000 objek. Meskipun Anda memiliki enum yang sangat besar dengan ribuan konstanta, Anda dapat yakin bahwa efek kinerja akan diabaikan di samping pembengkakan kerangka kerja Android itu sendiri.
Kevin Krumwiede
58

Jika enum hanya memiliki nilai, Anda harus mencoba menggunakan IntDef / StringDef, seperti yang ditunjukkan di sini:

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

Contoh: alih-alih:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

Kau gunakan:

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

dan dalam fungsi yang memilikinya sebagai parameter / nilai yang dikembalikan, gunakan:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

Jika enumnya kompleks, gunakan enum. Tidak seburuk itu.

Untuk membandingkan enum vs nilai konstanta, Anda harus membaca di sini:

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

Contohnya adalah enum dengan 2 nilai. Dibutuhkan 1112 byte dalam file dex dibandingkan dengan 128 byte ketika bilangan bulat konstan digunakan. Masuk akal, karena enum adalah kelas nyata, bukan cara kerjanya di C / C ++.

pengembang android
sumber
Meskipun saya menghargai jawaban lain yang memberikan pro / kontra dari enum, jawaban ini seharusnya menjadi jawaban yang diterima karena memberikan solusi terbaik untuk Android, khususnya. Anotasi dukungan adalah cara yang harus dilakukan. Kami, sebagai pengembang Android seharusnya berpikir "kecuali saya memiliki alasan kuat untuk menggunakan enum, saya akan menggunakan konstanta dengan anotasi", daripada 'cara berpikir jawaban lain "kecuali saya mulai khawatir tentang memori / kinerja nanti, Saya bisa menggunakan enum ". Hentikan diri Anda sekarang dari masalah kinerja di kemudian hari!
w3bshark
3
@ w3bshark Jika Anda mengalami masalah kinerja, enum tidak mungkin menjadi hal pertama yang harus Anda pikirkan untuk menyelesaikannya. Anotasi memiliki kelebihannya sendiri: tidak memiliki dukungan kompiler, tidak dapat memiliki kolom dan metode sendiri, membosankan untuk ditulis, Anda dapat dengan mudah lupa memberi anotasi int, dan seterusnya. Mereka bukanlah solusi yang lebih baik secara keseluruhan, mereka hanya ada untuk menghemat ruang heap saat Anda membutuhkannya.
Malcolm
1
@androiddeveloper Anda tidak dapat membuat kesalahan dengan meneruskan konstanta yang tidak ditentukan dalam deklarasi enum. Kode ini tidak akan pernah bisa dikompilasi. Jika Anda melewatkan konstanta yang salah dengan IntDefanotasi, itu akan terjadi. Mengenai jam tangan, menurut saya cukup jelas juga. Perangkat mana yang memiliki lebih banyak daya CPU, RAM, dan baterai: telepon atau jam tangan? Dan mana yang membutuhkan perangkat lunak agar kinerjanya lebih optimal?
Malcolm
3
@androiddeveloper OK, jika Anda bersikeras pada terminologi yang ketat, dengan kesalahan yang saya maksud adalah kesalahan yang bukan kesalahan waktu kompilasi (kesalahan run-time atau logika program). Apakah Anda mengolok-olok saya atau benar-benar tidak mengerti perbedaan antara mereka dan manfaat dari kesalahan waktu kompilasi? Ini adalah topik yang didiskusikan dengan baik, lihat ini di web. Mengenai jam tangan, Android menyertakan lebih banyak perangkat, tetapi yang paling terbatas adalah penyebut umum terendah.
Malcolm
2
@androiddeveloper Saya sudah menanggapi kedua pernyataan tersebut, mengulanginya sekali lagi tidak membatalkan apa yang saya katakan.
Malcolm
12

Selain jawaban sebelumnya, saya akan menambahkan bahwa jika Anda menggunakan Proguard (dan Anda harus melakukannya untuk mengurangi ukuran dan mengaburkan kode Anda), maka Anda Enumsakan secara otomatis dikonversi ke @IntDefmana pun memungkinkan:

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

kelas / unboxing / enum

Sederhanakan tipe enum menjadi konstanta integer, jika memungkinkan.

Oleh karena itu, jika Anda memiliki beberapa nilai diskrit dan beberapa metode hanya memungkinkan untuk mengambil nilai ini dan bukan yang lain dari jenis yang sama, maka saya akan menggunakan Enum, karena Proguard akan membuat manual ini bekerja mengoptimalkan kode untuk saya.

Dan berikut ini postingan yang bagus tentang penggunaan enum dari Jake Wharton, lihatlah.

Sebagai pengembang perpustakaan, saya menyadari pengoptimalan kecil ini yang harus dilakukan karena kami ingin berdampak sekecil mungkin pada ukuran, memori, dan kinerja aplikasi yang dikonsumsi. Tapi penting untuk menyadari bahwa [...] menempatkan enum di API publik vs. nilai integer jika sesuai tidak masalah. Mengetahui perbedaan untuk membuat keputusan yang tepat adalah yang terpenting

Gaket
sumber
11

Haruskah saya benar-benar menghindari penggunaan enum di Android?

Tidak. " Strictly " artinya sangat buruk, tidak boleh digunakan sama sekali. Mungkin masalah kinerja mungkin muncul dalam situasi ekstrim seperti banyak banyak (ribuan atau jutaan) operasi dengan enum (berturut-turut pada utas ui). Jauh lebih umum adalah jaringan operasi I / O yang harus ketat terjadi di thread latar belakang. Penggunaan enum yang paling umum mungkin adalah semacam pemeriksaan tipe - apakah suatu objek adalah ini atau itu yang begitu cepat sehingga Anda tidak akan dapat melihat perbedaan antara satu perbandingan enum dan perbandingan bilangan bulat.

Bisakah seseorang menjelaskan cara terbaik untuk merepresentasikan hal yang sama di Android?

Tidak ada aturan umum untuk ini. Gunakan apa pun yang sesuai untuk Anda dan membantu Anda menyiapkan aplikasi. Optimalkan nanti - setelah Anda melihat ada hambatan yang memperlambat beberapa aspek aplikasi Anda.

stan0
sumber
1
Mengapa Anda harus mengoptimalkan nanti, padahal menggunakan konstanta semudah itu dengan anotasi dukungan dan sekarang dioptimalkan? Lihat jawaban @ android_developer di sini.
w3bshark
4

Dua fakta.

1, Enum adalah salah satu fitur paling kuat di JAVA.

2, ponsel Android biasanya memiliki BANYAK memori.

Jadi jawaban saya TIDAK. Saya akan menggunakan Enum di Android.

Kai Wang
sumber
2
Jika Android memiliki BANYAK memori, itu tidak berarti aplikasi Anda harus menghabiskan semuanya dan tidak membiarkan yang lain. Anda harus mengikuti praktik terbaik untuk menyelamatkan aplikasi Anda agar tidak terbunuh oleh OS Android. Saya tidak mengatakan jangan gunakan enum, tapi gunakan hanya jika Anda tidak punya alternatif.
Varundroid
1
Saya setuju dengan Anda bahwa kita harus mengikuti praktik terbaik, namun, praktik terbaik tidak selalu berarti menggunakan sedikit memori. Menjaga kode tetap bersih, mudah dipahami jauh lebih penting daripada menyimpan beberapa k memori. Anda perlu menemukan keseimbangan.
Kai Wang
1
Saya setuju dengan Anda bahwa kita perlu menemukan keseimbangan tetapi menggunakan TypeDef tidak akan membuat kode kita terlihat buruk atau kurang dapat dipelihara. Saya menggunakannya selama lebih dari satu tahun sekarang dan tidak pernah merasa kode saya tidak mudah dipahami, juga tidak ada rekan saya yang mengeluh bahwa TypeDef sulit dipahami dibandingkan dengan enum. Saran saya adalah gunakan enum hanya ketika Anda tidak memiliki pilihan lain tetapi hindari jika Anda bisa.
Varundroid
2

Saya ingin menambahkan, bahwa Anda tidak dapat menggunakan @Annotation saat mendeklarasikan List <> atau Map <> di mana salah satu kunci atau nilai adalah salah satu antarmuka anotasi Anda. Anda mendapatkan pesan kesalahan "Anotasi tidak diizinkan di sini".

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

Jadi ketika Anda perlu mengemasnya ke dalam daftar / peta, gunakan enum, karena mereka dapat ditambahkan, tetapi @annotated int / string groups tidak bisa.

Grisgram
sumber