Tôi có nên tuyệt đối tránh sử dụng enum trên Android không?


93

Tôi đã sử dụng để xác định một tập hợp các hằng số liên quan như Bundlecác khóa với nhau trong một giao diện như bên dưới:

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

Điều này cung cấp cho tôi một cách tốt hơn để nhóm các hằng số có liên quan với nhau và sử dụng chúng bằng cách thực hiện nhập tĩnh (không phải thực hiện). Tôi biết Androidkhuôn khổ cũng sử dụng các hằng số trong cùng một cách như Toast.LENTH_LONG, View.GONE.

Tuy nhiên, tôi thường cảm thấy rằng cách Java Enumscung cấp tốt hơn và mạnh mẽ hơn nhiều để biểu diễn hằng số.

Nhưng có vấn đề về độ bền khi sử dụng enumstrên Androidkhông?

Với một chút nghiên cứu, cuối cùng tôi đã bối rối. Từ câu hỏi "Tránh Enums nơi bạn chỉ cần kiến" này đã bị xóa khỏi các mẹo về hiệu suất của Android? Rõ ràng là Googleđã xóa "Tránh enums" khỏi các mẹo về hiệu suất của nó, nhưng từ tài liệu đào tạo chính thức của nó, hãy lưu ý phần chi phí bộ nhớ có ghi rõ ràng: "Enums thường yêu cầu bộ nhớ nhiều hơn gấp đôi so với hằng số tĩnh. Bạn nên tránh tuyệt đối việc sử dụng enum trên Android. " Điều này vẫn tốt chứ? (Nói trong Javacác phiên bản sau 1.6)

Một vấn đề nữa mà tôi quan sát thấy là để gửi enumsqua intentssử dụng, Bundletôi nên gửi chúng bằng cách tuần tự hóa (tức là putSerializable()tôi nghĩ rằng một hoạt động đắt tiền so với putString()phương pháp nguyên thủy , eventhough enumscung cấp nó miễn phí).

Ai đó có thể vui lòng làm rõ cái nào là cách tốt nhất để đại diện giống nhau Androidkhông? Tôi có nên tuyệt đối tránh sử dụng enumstrên Androidkhông?


5
Bạn nên sử dụng các công cụ mà bạn có sẵn. Trên thực tế, một hoạt động hoặc phân đoạn chiếm rất nhiều bộ nhớ và việc sử dụng cpu, nhưng không có lý do gì để ngừng sử dụng chúng. Sử dụng một int tĩnh nếu bạn chỉ cần điều đó và sử dụng enum khi bạn cần.
Patrick

11
Tôi đồng ý. Điều này có vẻ như tối ưu hóa quá sớm. Trừ khi bạn đang gặp vấn đề về hiệu suất và / hoặc bộ nhớ và có thể chứng minh thông qua hồ sơ rằng enums là nguyên nhân, hãy sử dụng chúng ở những nơi có ý nghĩa.
GreyBeardGeek

2
Người ta đã từng tin rằng enums gây ra hình phạt cho hiệu suất không phải là ba lần, nhưng các điểm chuẩn gần đây hơn cho thấy không có lợi cho việc sử dụng hằng số. Xem stackoverflow.com/questions/24491160/… cũng như stackoverflow.com/questions/5143256/…
Benjamin Sergent

1
Để tránh hình phạt về hiệu suất của việc tuần tự hóa một Enum trong một gói, bạn có thể chuyển nó dưới dạng int bằng cách sử dụng Enum.ordinal()thay thế.
BladeCoder

1
cuối cùng, có một số giải thích về các vấn đề hiệu suất trên Enum tại đây youtube.com/watch?v=Hzs6OBcvNQE
nvinayshetty

Câu trả lời:


116

Sử dụng enumkhi bạn cần các tính năng của nó. Đừng tránh nó một cách nghiêm ngặt .

Java enum mạnh hơn, nhưng nếu bạn không cần các tính năng của nó, hãy sử dụng hằng số, chúng chiếm ít dung lượng hơn và chúng có thể là nguyên thủy.

Khi nào sử dụng enum:

  • gõ kiểm tra - bạn có thể chấp nhận chỉ giá trị niêm yết, và họ không liên tục (xem dưới đây những gì tôi gọi là liên tục tại đây)
  • nạp chồng phương thức - mọi hằng số enum đều có cách triển khai phương thức riêng của nó

    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);
    }
  • nhiều dữ liệu hơn - một hằng số của bạn chứa nhiều thông tin không thể được đưa vào một biến

  • dữ liệu phức tạp - nhu cầu liên tục của bạn các phương pháp để vận hành trên dữ liệu

Khi nào không sử dụng enum:

  • bạn có thể chấp nhận tất cả các giá trị của một kiểu và hằng số của bạn chỉ chứa những giá trị được sử dụng nhiều nhất này
  • bạn có thể chấp nhận dữ liệu liên tục

    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);
            }
    
            ...
        }
    }
  • cho tên (như trong ví dụ của bạn)
  • cho mọi thứ khác mà thực sự không cần enum

Enums chiếm nhiều không gian hơn

  • một tham chiếu duy nhất đến một hằng số enum chiếm 4 byte
  • mọi hằng số enum chiếm không gian là tổng kích thước của các trường của nó được căn chỉnh thành 8 byte + chi phí của đối tượng
  • bản thân lớp enum chiếm một số không gian

Hằng số chiếm ít không gian hơn

  • một hằng số không có tham chiếu vì vậy nó là một dữ liệu thuần túy (ngay cả khi nó là một tham chiếu, thì trường hợp enum sẽ là một tham chiếu đến một tham chiếu khác)
  • hằng số có thể được thêm vào một lớp hiện có - không cần thiết phải thêm một lớp khác
  • hằng số có thể được nội dòng; nó mang đến các tính năng thời gian biên dịch kéo dài (chẳng hạn như kiểm tra null, tìm mã chết, v.v.)

2
Bạn có thể sử dụng chú thích @IntDef để mô phỏng việc kiểm tra kiểu cho các hằng số int. Đó là một lập luận ít có lợi cho Java Enums.
BladeCoder

3
@BladeCoder không, bạn không cần một tham chiếu đến liên tục
Kamil Jarosz

1
Ngoài ra, cần lưu ý rằng việc sử dụng enums thúc đẩy việc sử dụng phản xạ. Và được biết rằng việc sử dụng phản xạ là một hiệu suất LỚN đối với Android. Xem tại đây: blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark

3
@ w3bshark Làm thế nào để enums thúc đẩy việc sử dụng phản xạ?
Kevin Krumwiede

3
Một điều khác cần ghi nhớ là một kết xuất đống từ trống tiêu chuẩn "Hello, world!" dự án bao gồm hơn 4.000 lớp và 700.000 đối tượng. Ngay cả khi bạn có một enum khổng lồ với hàng nghìn hằng số, bạn có thể chắc chắn rằng hiệu quả hoạt động sẽ không đáng kể bên cạnh sự cồng kềnh của chính Android framework.
Kevin Krumwiede

57

Nếu enums chỉ có giá trị, bạn nên thử sử dụng IntDef / StringDef, như được hiển thị ở đây:

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

Ví dụ: thay vì:

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

bạn sử dụng:

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

và trong hàm có nó làm tham số / giá trị trả về, hãy sử dụng:

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

Trong trường hợp enum phức tạp, hãy sử dụng một enum. Nó không tệ.

Để so sánh enums với giá trị không đổi, bạn nên đọc ở đây:

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

Ví dụ của họ là một enum có 2 giá trị. Nó chiếm 1112 byte trong tệp dex so với 128 byte khi sử dụng số nguyên hằng số. Có lý, vì enums là các lớp thực, trái ngược với cách nó hoạt động trên C / C ++.


Mặc dù tôi đánh giá cao các câu trả lời khác cung cấp ưu / nhược điểm của enums, nhưng câu trả lời này phải là câu trả lời được chấp nhận vì nó cung cấp giải pháp tốt nhất cho Android. Chú thích hỗ trợ là cách để đi. Chúng tôi, với tư cách là các nhà phát triển Android nên nghĩ "trừ khi tôi có lý do chính đáng để sử dụng enums, tôi sẽ sử dụng hằng số với chú thích", thay vì cách suy nghĩ của các câu trả lời khác "trừ khi tôi bắt đầu lo lắng về bộ nhớ / hiệu suất sau này, Tôi có thể sử dụng enums ”. Hãy ngăn bản thân gặp vấn đề về hiệu suất sau này!
w3bshark

3
@ w3bshark Nếu bạn đang gặp vấn đề về hiệu suất, enums có thể không phải là điều đầu tiên mà bạn nên nghĩ đến để giải quyết chúng. Chú thích có sự đánh đổi của riêng chúng: chúng không có hỗ trợ trình biên dịch, chúng không thể có các trường và phương thức riêng, chúng rất tẻ nhạt khi viết, bạn có thể dễ dàng quên chú thích một int, v.v. Nhìn chung, chúng không phải là một giải pháp tốt hơn, chúng chỉ ở đó để tiết kiệm dung lượng lưu trữ khi bạn cần.
Malcolm

1
@androiddeveloper Bạn không thể mắc lỗi khi chuyển một hằng số không được định nghĩa trong khai báo enum. Mã này sẽ không bao giờ được biên dịch. Nếu bạn chuyển một hằng số sai với một IntDefchú thích, nó sẽ xảy ra. Đối với đồng hồ, tôi nghĩ nó cũng khá rõ ràng. Thiết bị nào có nhiều năng lượng CPU, RAM và pin hơn: điện thoại hay đồng hồ? Và cái nào cần phần mềm để tối ưu hơn về hiệu suất vì điều đó?
Malcolm

3
@androiddeveloper OK, nếu bạn nhấn mạnh vào thuật ngữ nghiêm ngặt, do nhầm lẫn, tôi có nghĩa là lỗi không phải lỗi thời gian biên dịch (lỗi thời gian chạy hoặc lỗi logic chương trình). Bạn đang troll tôi hay thực sự không hiểu sự khác biệt giữa chúng và lợi ích của lỗi thời gian biên dịch? Đây là một chủ đề được thảo luận nhiều, hãy tìm kiếm điều này trên web. Về đồng hồ, Android có nhiều thiết bị hơn, nhưng những thiết bị hạn chế nhất sẽ là mẫu số chung thấp nhất.
Malcolm

2
@androiddeveloper Tôi đã trả lời cả hai câu đó, việc lặp lại chúng một lần nữa không làm mất hiệu lực những gì tôi đã nói.
Malcolm

12

Ngoài các câu trả lời trước, tôi sẽ nói thêm rằng nếu bạn đang sử dụng Proguard (và bạn chắc chắn nên làm điều đó để giảm kích thước và làm xáo trộn mã của mình), thì của bạn Enumssẽ được tự động chuyển đổi thành @IntDefbất cứ nơi nào có thể:

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

class / unboxing / enum

Đơn giản hóa các kiểu enum thành hằng số nguyên, bất cứ khi nào có thể.

Do đó, nếu bạn có một số giá trị rời rạc và một số phương thức chỉ cho phép lấy các giá trị này chứ không phải các giá trị khác cùng loại, thì tôi sẽ sử dụng Enum, vì Proguard sẽ thực hiện công việc tối ưu hóa mã thủ công này cho tôi.

đây là một bài viết hay về cách sử dụng enums từ Jake Wharton, hãy xem nó.

Với tư cách là nhà phát triển thư viện, tôi nhận ra những tối ưu hóa nhỏ này nên được thực hiện khi chúng tôi muốn tác động ít nhất đến kích thước, bộ nhớ và hiệu suất của ứng dụng tiêu thụ càng tốt. Nhưng điều quan trọng là phải nhận ra rằng [...] việc đặt một enum trong API công khai của bạn so với các giá trị số nguyên nếu thích hợp là hoàn toàn tốt. Biết sự khác biệt để đưa ra quyết định sáng suốt là điều quan trọng


11

Tôi có nên tuyệt đối tránh sử dụng enum trên Android không?

Không. " Nghiêm túc " có nghĩa là chúng quá tệ, chúng hoàn toàn không nên được sử dụng. Có thể một vấn đề về hiệu suất có thể phát sinh trong một tình huống cực đoan như nhiều (hàng nghìn hoặc hàng triệu) hoạt động với enum (liên tiếp trên chuỗi ui). Phổ biến hơn rất nhiều là các hoạt động I / O mạng sẽ diễn ra nghiêm ngặt trong một chuỗi nền. Việc sử dụng phổ biến nhất của sự đếm có lẽ là một số loại kiểu kiểm tra - cho dù một đối tượng là này hay rằng đó là quá nhanh, bạn sẽ không thể nhận thấy một sự khác biệt giữa một so sánh duy nhất của sự đếm và so sánh các số nguyên.

Ai đó có thể vui lòng làm rõ cái nào là cách tốt nhất để đại diện giống nhau trong Android không?

Không có quy tắc chung cho điều này. Sử dụng bất kỳ thứ gì phù hợp với bạn và giúp bạn sẵn sàng ứng dụng. Tối ưu hóa sau - sau khi bạn nhận thấy có một nút thắt cổ chai làm chậm một số khía cạnh của ứng dụng của bạn.


1
Tại sao bạn lại tối ưu hóa sau, khi việc sử dụng hằng số với chú thích hỗ trợ và tối ưu hóa ngay bây giờ cũng dễ dàng như vậy? Xem câu trả lời của @ android_developer tại đây.
w3bshark

11

Với Android P, google không có hạn chế / phản đối trong việc sử dụng enums

Tài liệu đã thay đổi nơi trước đây được khuyến cáo nên thận trọng nhưng hiện tại nó không đề cập đến. https://developer.android.com/reference/java/lang/Enum


1
Có bằng chứng nào khác ngoài điều này không: Tôi đã tìm thấy tuyên bố của @JakeWharton về điều đó: twitter.com/jakewharton/status/1067790191237181441 . Bất kỳ ai cũng có thể kiểm tra bytecode.
soshial

4

Hai sự thật.

1, Enum là một trong những tính năng mạnh nhất trong JAVA.

2, Điện thoại Android thường có rất nhiều bộ nhớ.

Vì vậy, câu trả lời của tôi là KHÔNG. Tôi sẽ sử dụng Enum trong Android.


2
Nếu Android có NHIỀU bộ nhớ, điều đó không có nghĩa là ứng dụng của bạn sẽ sử dụng hết bộ nhớ và không để lại cho người khác. Bạn nên làm theo các phương pháp hay nhất để cứu ứng dụng của mình khỏi bị hệ điều hành Android giết. Tôi không nói rằng không sử dụng enum, nhưng chỉ sử dụng khi bạn không có giải pháp thay thế.
Varundroid

1
Tôi đồng ý với bạn rằng chúng ta nên làm theo phương pháp tốt nhất, tuy nhiên, phương pháp tốt nhất không phải lúc nào cũng có nghĩa là sử dụng ít bộ nhớ nhất. Giữ mã sạch sẽ, dễ hiểu quan trọng hơn nhiều so với tiết kiệm vài k bộ nhớ. Bạn cần tìm sự cân bằng.
Kai Wang

1
Tôi đồng ý với bạn rằng chúng ta cần tìm sự cân bằng nhưng sử dụng TypeDef sẽ không làm cho mã của chúng ta trông xấu hoặc kém khả năng bảo trì. Tôi đang sử dụng nó hơn một năm nay và chưa bao giờ cảm thấy mã của mình không dễ hiểu, cũng không ai trong số các đồng nghiệp của tôi từng phàn nàn rằng TypeDefs khó hiểu so với enums. Lời khuyên của tôi là chỉ sử dụng enums khi bạn không còn lựa chọn nào khác nhưng hãy tránh nó nếu bạn có thể.
Varundroid

2

Tôi muốn nói thêm rằng bạn không thể sử dụng @Annotations khi bạn khai báo Danh sách <> hoặc Bản đồ <> trong đó khóa hoặc giá trị là của một trong các giao diện chú thích của bạn. Bạn gặp lỗi "Chú thích không được phép ở đây".

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

Vì vậy, khi bạn cần đóng gói nó thành một danh sách / bản đồ, hãy sử dụng enum, vì chúng có thể được thêm vào, nhưng các nhóm int / string @annotated thì không.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.