Vượt qua enum hoặc đối tượng thông qua một ý định (giải pháp tốt nhất)


221

Tôi có một hoạt động mà khi bắt đầu cần truy cập vào hai ArrayLists khác nhau. Cả hai Danh sách là các Đối tượng khác nhau tôi đã tự tạo.

Về cơ bản tôi cần một cách để truyền các đối tượng này vào hoạt động từ một Ý định. Tôi có thể sử dụng addExtras () nhưng điều này đòi hỏi một lớp tương thích có thể điều chỉnh được. Tôi có thể làm cho các lớp học của tôi được thông qua tuần tự nhưng vì tôi hiểu điều này làm chậm chương trình.

Những lựa chọn của tôi là gì?

Tôi có thể vượt qua Enum không?

Như một bên: có cách nào để truyền tham số cho Trình xây dựng hoạt động từ một ý định không?


Có lẽ tôi đang thiếu một cái gì đó, nhưng làm thế nào một enum liên quan đến một ArrayList?
Martin Konecny

Câu trả lời:


558

Đây là một câu hỏi cũ, nhưng mọi người đều không đề cập đến việc Enums thực sự Serializablevà do đó hoàn toàn có thể được thêm vào Ý định như một phần phụ. Như thế này:

public enum AwesomeEnum {
  SOMETHING, OTHER;
}

intent.putExtra("AwesomeEnum", AwesomeEnum.SOMETHING);

AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum");

Đề xuất sử dụng các biến tĩnh hoặc toàn ứng dụng là một ý tưởng thực sự tồi tệ. Điều này thực sự kết hợp các hoạt động của bạn với một hệ thống quản lý nhà nước và rất khó để duy trì, gỡ lỗi và ràng buộc vấn đề.


THAY ĐỔI:

Một điểm tốt đã được chú ý bởi tedzyc về thực tế là giải pháp do Oderik cung cấp cho bạn một lỗi. Tuy nhiên, sự thay thế được cung cấp là một chút rườm rà để sử dụng (thậm chí sử dụng thuốc generic).

Nếu bạn thực sự lo lắng về hiệu suất của việc thêm enum vào Intent, tôi đề xuất các phương án thay thế:

LỰA CHỌN 1:

public enum AwesomeEnum {
  SOMETHING, OTHER;
  private static final String name = AwesomeEnum.class.getName();
  public void attachTo(Intent intent) {
    intent.putExtra(name, ordinal());
  }
  public static AwesomeEnum detachFrom(Intent intent) {
    if(!intent.hasExtra(name)) throw new IllegalStateException();
    return values()[intent.getIntExtra(name, -1)];
  }
}

Sử dụng:

// Sender usage
AwesomeEnum.SOMETHING.attachTo(intent);
// Receiver usage
AwesomeEnum result = AwesomeEnum.detachFrom(intent);

TÙY CHỌN 2: (chung chung, có thể tái sử dụng và tách rời khỏi enum)

public final class EnumUtil {
    public static class Serializer<T extends Enum<T>> extends Deserializer<T> {
        private T victim;
        @SuppressWarnings("unchecked") 
        public Serializer(T victim) {
            super((Class<T>) victim.getClass());
            this.victim = victim;
        }
        public void to(Intent intent) {
            intent.putExtra(name, victim.ordinal());
        }
    }
    public static class Deserializer<T extends Enum<T>> {
        protected Class<T> victimType;
        protected String name;
        public Deserializer(Class<T> victimType) {
            this.victimType = victimType;
            this.name = victimType.getName();
        }
        public T from(Intent intent) {
            if (!intent.hasExtra(name)) throw new IllegalStateException();
            return victimType.getEnumConstants()[intent.getIntExtra(name, -1)];
        }
    }
    public static <T extends Enum<T>> Deserializer<T> deserialize(Class<T> victim) {
        return new Deserializer<T>(victim);
    }
    public static <T extends Enum<T>> Serializer<T> serialize(T victim) {
        return new Serializer<T>(victim);
    }
}

Sử dụng:

// Sender usage
EnumUtil.serialize(AwesomeEnum.Something).to(intent);
// Receiver usage
AwesomeEnum result = 
EnumUtil.deserialize(AwesomeEnum.class).from(intent);

TÙY CHỌN 3 (với Kotlin):

Đã được một thời gian, nhưng kể từ bây giờ chúng tôi có Kotlin, tôi nghĩ rằng tôi sẽ thêm một tùy chọn khác cho mô hình mới. Ở đây chúng ta có thể sử dụng các hàm mở rộng và các kiểu hợp nhất (giữ lại kiểu khi biên dịch).

inline fun <reified T : Enum<T>> Intent.putExtra(victim: T): Intent =
    putExtra(T::class.java.name, victim.ordinal)

inline fun <reified T: Enum<T>> Intent.getEnumExtra(): T? =
    getIntExtra(T::class.java.name, -1)
        .takeUnless { it == -1 }
        ?.let { T::class.java.enumConstants[it] }

Có một vài lợi ích của việc làm theo cách này.

  • Chúng tôi không yêu cầu "chi phí chung" của một đối tượng trung gian để thực hiện tuần tự hóa vì tất cả đều được thực hiện nhờ vào inlineđó sẽ thay thế các cuộc gọi bằng mã bên trong hàm.
  • Các chức năng quen thuộc hơn vì chúng tương tự như các SDK.
  • IDE sẽ tự động hoàn thành các chức năng này, điều đó có nghĩa là không cần phải có kiến ​​thức trước đó về lớp tiện ích.

Một trong những nhược điểm là, nếu chúng ta thay đổi thứ tự của Emums, thì mọi tham chiếu cũ sẽ không hoạt động. Đây có thể là một vấn đề với những thứ như Ý định bên trong ý định đang chờ xử lý vì chúng có thể tồn tại trong các bản cập nhật. Tuy nhiên, trong thời gian còn lại, nó sẽ ổn thôi.

Điều quan trọng cần lưu ý là các giải pháp khác, như sử dụng tên thay vì vị trí, cũng sẽ thất bại nếu chúng tôi đổi tên bất kỳ giá trị nào. Mặc dù, trong những trường hợp đó, chúng tôi nhận được một ngoại lệ thay vì giá trị Enum không chính xác.

Sử dụng:

// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()

14
+1 để chỉ ra rằng đó là một "ý tưởng tồi thực sự" để làm cho chúng trở nên rộng rãi.
bugfixr

3
Tôi thực sự đã làm việc trong một dự án mà tôi không muốn xử lý việc tuần tự hóa hoặc bó các đối tượng (rất nhiều đối tượng có nhiều biến trong đó) và sử dụng các biến toàn cục tĩnh là ổn ... cho đến khi một đồng đội bước vào dự án. Chi phí cố gắng phối hợp việc sử dụng những quả cầu đó đã khiến tôi phải "vặn nó. Tôi đang viết một trình tạo mã để làm cho tôi một số Parcelables". Số lượng lỗi giảm đáng kể
Joe Plante

2
@Coeffect Vâng, đó là một đề xuất dễ hiểu, nhưng trong hầu hết các trường hợp, điều này có thể đủ điều kiện là tối ưu hóa sớm trừ khi bạn phân tích hàng ngàn enum (mà về bản chất chúng chỉ nên được sử dụng để xử lý trạng thái) Trên Nexus 4 bạn nhận được cải thiện 1ms ( developerphil.com/parcelable-vs-serializable ) không chắc chắn rằng nó đáng để làm thêm chân, nhưng sau đó bạn lại có các lựa chọn thay thế khác mà tôi đã đề xuất;)
pottaisco

1
@rgv Dưới mui xe Kotlin biên dịch enum classcác loại thành Java đơn giản enum. Tôi đoán cách giải quyết dễ dàng hơn là enum classthực hiện Serializable: enum class AwesomeEnum : Serializable { A, B, C }Không lý tưởng, nhưng nên hoạt động.
pottaisco

1
@Pierre như với bất kỳ câu trả lời tốt, có một "nó phụ thuộc". Không cần phải gia hạn nối tiếp. Câu trả lời ban đầu là hợp lệ. Những lựa chọn bổ sung đó là trong trường hợp bạn có trường hợp có thể có cổ chai, giống như nếu bạn đang giải trừ hàng triệu hồ sơ (hy vọng là không). Sử dụng những gì bạn thấy phù hợp ...
pottaisco

114

Bạn có thể làm cho enum của bạn thực hiện Parcelable, điều này khá dễ dàng cho enum:

public enum MyEnum implements Parcelable {
    VALUE;


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(final Parcel dest, final int flags) {
        dest.writeInt(ordinal());
    }

    public static final Creator<MyEnum> CREATOR = new Creator<MyEnum>() {
        @Override
        public MyEnum createFromParcel(final Parcel source) {
            return MyEnum.values()[source.readInt()];
        }

        @Override
        public MyEnum[] newArray(final int size) {
            return new MyEnum[size];
        }
    };
}

Sau đó, bạn có thể sử dụng Intent.putExtra (Chuỗi, Parcelable).

CẬP NHẬT: Xin lưu ý nhận xét của người phá hoại enum.values()phân bổ một mảng mới tại mỗi cuộc gọi.

CẬP NHẬT: Android Studio có một mẫu trực tiếp ParcelableEnumthực hiện giải pháp này. (Trên Windows, sử dụng Ctrl+ J)


3
Cũng có thể các phương thức toString () và valueOf () của enum thay vì các lệnh.
Natix

2
Sử dụng ordinal () có thể bị hỏng, khi nhà phát triển chèn thành viên enum mới. Tất nhiên, đổi tên thành viên enum cũng sẽ phá vỡ tên (). Nhưng, các nhà phát triển có nhiều khả năng chèn thành viên mới, thay vì đổi tên, vì đổi tên đòi hỏi anh ta phải tính lại toàn bộ dự án.
Cheok Yan Cheng

2
Tôi không đồng ý rằng các giá trị enum bổ sung (hoặc sắp xếp lại) có nhiều khả năng hơn các giá trị được đổi tên. Sử dụng một IDE tinh vi như tái cấu trúc IntelliJ IDEA không phải là vấn đề lớn. Nhưng quan điểm của bạn vẫn tốt: bạn phải đảm bảo rằng việc tuần tự hóa là nhất quán trong suốt quá trình thực hiện cùng tồn tại. Điều đó đúng với bất kỳ loại tuần tự hóa nào. Tôi cho rằng trong hầu hết các trường hợp, các bưu kiện được chuyển qua một ứng dụng trong đó chỉ có một triển khai tồn tại, do đó không phải là một vấn đề.
Oderik

2
value () tạo ra một mảng mới trên mỗi cuộc gọi, vì vậy tốt nhất để lưu nó vào ví dụ. một mảng tĩnh riêng
wreckgar23

2
Thực tế là trong các trường hợp chung (như lưu trữ db), ordinal () không an toàn không liên quan đến bưu kiện Android. Bưu kiện không phải để lưu trữ lâu dài (liên tục). Họ chết với ứng dụng. Vì vậy, khi bạn thêm / đổi tên một enum, bạn sẽ nhận được bưu kiện mới.
noamtm

24

Bạn có thể vượt qua một enum thông qua như một chuỗi.

public enum CountType {
    ONE,
    TWO,
    THREE
}

private CountType count;
count = ONE;

String countString = count.name();

CountType countToo = CountType.valueOf(countString);

Các chuỗi đã cho được hỗ trợ, bạn sẽ có thể chuyển giá trị của enum xung quanh mà không gặp vấn đề gì.


3
Việc thực hiện đơn giản nhất của tất cả chúng.
Avi Cohen

22

Để truyền enum theo ý định, bạn có thể chuyển enum thành số nguyên.

Ví dụ:

public enum Num{A ,B}

Gửi (enum đến số nguyên):

Num send = Num.A;
intent.putExtra("TEST", send.ordinal());

Nhận (số nguyên đến enum):

Num rev;
int temp = intent.getIntExtra("TEST", -1);
if(temp >= 0 && temp < Num.values().length)
    rev = Num.values()[temp];

Trân trọng. :)


8
hoặc bạn có thể gửi chuỗi dưới dạng chuỗi (để có thể đọc được) bằng Num.A.name () và sau đó lấy lại bằng Num.ValueOf (aim.getStringExtra ("TEST"))
Benoit Jadinon

1
Tôi nghĩ rằng cách của Benoit an toàn hơn, vì temp.ordinal () không được ưa thích trong thực tế vì giá trị thứ tự () có thể thay đổi. Xem bài đăng này: stackoverflow.com/questions/2836256/
Mạnh

15

Nếu bạn thực sự cần, bạn có thể tuần tự hóa một enum dưới dạng Chuỗi, sử dụng name()valueOf(String), như sau:

 class Example implements Parcelable { 
   public enum Foo { BAR, BAZ }

   public Foo fooValue;

   public void writeToParcel(Parcel dest, int flags) {
      parcel.writeString(fooValue == null ? null : fooValue.name());
   }

   public static final Creator<Example> CREATOR = new Creator<Example>() {
     public Example createFromParcel(Parcel source) {        
       Example e = new Example();
       String s = source.readString(); 
       if (s != null) e.fooValue = Foo.valueOf(s);
       return e;
     }
   }
 }

Điều này rõ ràng là không hoạt động nếu enum của bạn có trạng thái có thể thay đổi (mà thực tế họ không nên).


4

Có thể làm cho Enum của bạn triển khai Nối tiếp, sau đó bạn có thể chuyển nó qua Ý định, vì có một phương thức để chuyển nó dưới dạng tuần tự hóa. Lời khuyên để sử dụng int thay vì enum là không có thật. Enums được sử dụng để làm cho mã của bạn dễ đọc hơn và dễ bảo trì hơn. Nó sẽ là một bước lùi lớn vào thời kỳ đen tối để không thể sử dụng Enums.


2
Bất kỳ loại Enum nào cũng mở rộng siêu lớp Enum theo mặc định, đã thực hiện Nối tiếp.
Arcao

2

về bài viết của Oderik:

Bạn có thể làm cho enum của bạn thực hiện Parcelable, điều này khá dễ dàng cho enum:

công khai enum MyEnum triển khai Parcelable {...} Bạn có thể sử dụng Intent.putExtra (Chuỗi, Parcelable).

Nếu bạn xác định biến MyEnum myEnum, sau đó thực hiện ý định.putExtra ("Parcelable1", myEnum), bạn sẽ nhận được thông báo lỗi "Phương thức putExtra (Chuỗi, Parcelable) không rõ ràng cho thông báo lỗi kiểu Intent". bởi vì cũng có một phương thức Intent.putExtra (String, Parcelable) và chính kiểu 'Enum' thực hiện giao diện serializable, do đó trình biên dịch không biết chọn phương thức nào (aim.putExtra (String, Parcelable / hoặc serializable)).

Đề nghị xóa giao diện Parcelable khỏi MyEnum và di chuyển mã lõi vào triển khai Parcelable của lớp bọc, như thế này (Father2 là Parcelable và chứa trường enum):

public class Father2 implements Parcelable {

AnotherEnum mAnotherEnum;
int mField;

public Father2(AnotherEnum myEnum, int field) {
    mAnotherEnum = myEnum;
    mField = field;
}

private Father2(Parcel in) {
    mField = in.readInt();
    mAnotherEnum = AnotherEnum.values()[in.readInt()];
}

public static final Parcelable.Creator<Father2> CREATOR = new Parcelable.Creator<Father2>() {

    public Father2 createFromParcel(Parcel in) {
        return new Father2(in);
    }

    @Override
    public Father2[] newArray(int size) {
        return new Father2[size];
    }

};

@Override
public int describeContents() {
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(mField);
    dest.writeInt(mAnotherEnum.ordinal());
}

}

sau đó chúng ta có thể làm:

AnotherEnum anotherEnum = AnotherEnum.Z;
intent.putExtra("Serializable2", AnotherEnum.X);   
intent.putExtra("Parcelable2", new Father2(AnotherEnum.X, 7));

5
Ví dụ, bạn có thể chọn chữ ký chính xác bằng cách "truyền" đối sốintent.putExtra("myEnum", (Parcelable) enumValue);
Oderik 18/03/13

Sử dụng thứ tự là hoàn hảo!
slott

Đây là một cách nói thực sự phức tạp bundle.putExtra("key", AnotherEnum.X.ordinal()).
TWiStErRob

2

bạn có thể sử dụng hàm tạo enum cho enum để có kiểu dữ liệu nguyên thủy ..

public enum DaysOfWeek {
    MONDAY(1),
    TUESDAY(2),
    WEDNESDAY(3),
    THURSDAY(4),
    FRIDAY(5),
    SATURDAY(6),
    SUNDAY(7);

    private int value;
    private DaysOfWeek(int value) {
        this.value = value;
    }

    public int getValue() {
        return this.value;
    }

    private static final SparseArray<DaysOfWeek> map = new SparseArray<DaysOfWeek>();

    static
    {
         for (DaysOfWeek daysOfWeek : DaysOfWeek.values())
              map.put(daysOfWeek.value, daysOfWeek);
    }

    public static DaysOfWeek from(int value) {
        return map.get(value);
    }
}

bạn có thể sử dụng để chuyển int thành phần bổ sung, sau đó kéo nó từ enum bằng giá trị của nó.


2

Hầu hết các câu trả lời đang sử dụng khái niệm Parcelable ở đây đều nằm trong mã Java. Nó dễ dàng hơn để làm điều đó trong Kotlin.

Chỉ cần chú thích lớp enum của bạn với @Parcelize và triển khai giao diện Parcelable.

@Parcelize
enum class ViewTypes : Parcelable {
TITLE, PRICES, COLORS, SIZES
}

1

Tôi thích đơn giản.

  • Hoạt động Fred có hai chế độ - HAPPYSAD.
  • Tạo một tĩnh IntentFactorytạo ra Intentcho bạn. Vượt qua nó Modebạn muốn.
  • Việc IntentFactorysử dụng tên của Modelớp làm tên của phần phụ.
  • Việc IntentFactorychuyển đổi Modesang Stringsử dụngname()
  • Khi nhập vào onCreatesử dụng thông tin này để chuyển đổi trở lại a Mode.
  • Bạn có thể sử dụng ordinal()Mode.values()là tốt. Tôi thích chuỗi vì tôi có thể thấy chúng trong trình gỡ lỗi.

    public class Fred extends Activity {
    
        public static enum Mode {
            HAPPY,
            SAD,
            ;
        }
    
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.betting);
            Intent intent = getIntent();
            Mode mode = Mode.valueOf(getIntent().getStringExtra(Mode.class.getName()));
            Toast.makeText(this, "mode="+mode.toString(), Toast.LENGTH_LONG).show();
        }
    
        public static Intent IntentFactory(Context context, Mode mode){
            Intent intent = new Intent();
            intent.setClass(context,Fred.class);
            intent.putExtra(Mode.class.getName(),mode.name());
    
            return intent;
        }
    }

tò mò .. ai gọi IntentFactory? bạn có thể giải thích về cách một hoạt động khác sẽ gọi Fred và làm thế nào Fred có thể đảm bảo rằng Chế độ được thông qua không?
erik

0

Tôi nghĩ rằng đặt cược tốt nhất của bạn sẽ là chuyển đổi các danh sách đó thành thứ gì đó có thể phân loại được, chẳng hạn như chuỗi (hoặc bản đồ?) Để đưa nó vào Hoạt động. Sau đó, Activity sẽ phải chuyển đổi nó trở lại thành một mảng.

Thực hiện các bưu kiện tùy chỉnh là một nỗi đau ở cổ IMHO vì vậy tôi sẽ tránh nó nếu có thể.


0

Xem xét sau enum ::

public static  enum MyEnum {
    ValueA,
    ValueB
}

Để vượt qua ::

 Intent mainIntent = new Intent(this,MyActivity.class);
 mainIntent.putExtra("ENUM_CONST", MyEnum.ValueA);
 this.startActivity(mainIntent);

Để lấy lại từ ý định / gói / đối số ::

 MyEnum myEnum = (MyEnum) intent.getSerializableExtra("ENUM_CONST");

0

Nếu bạn chỉ muốn gửi một enum, bạn có thể làm một cái gì đó như:

Đầu tiên khai báo một enum chứa một số giá trị (có thể được truyền qua ý định):

 public enum MyEnum {
    ENUM_ZERO(0),
    ENUM_ONE(1),
    ENUM_TWO(2),
    ENUM_THREE(3);
    private int intValue;

    MyEnum(int intValue) {
        this.intValue = intValue;
    }

    public int getIntValue() {
        return intValue;
    }

    public static MyEnum getEnumByValue(int intValue) {
        switch (intValue) {
            case 0:
                return ENUM_ZERO;
            case 1:
                return ENUM_ONE;
            case 2:
                return ENUM_TWO;
            case 3:
                return ENUM_THREE;
            default:
                return null;
        }
    }
}

Sau đó:

  intent.putExtra("EnumValue", MyEnum.ENUM_THREE.getIntValue());

Và khi bạn muốn lấy nó:

  NotificationController.MyEnum myEnum = NotificationController.MyEnum.getEnumByValue(intent.getIntExtra("EnumValue",-1);

Miếng bánh!


0

Sử dụng các chức năng mở rộng của Kotlin

inline fun <reified T : Enum<T>> Intent.putExtra(enumVal: T, key: String? = T::class.qualifiedName): Intent =
    putExtra(key, enumVal.ordinal)

inline fun <reified T: Enum<T>> Intent.getEnumExtra(key: String? = T::class.qualifiedName): T? =
    getIntExtra(key, -1)
        .takeUnless { it == -1 }
        ?.let { T::class.java.enumConstants[it] }

Điều này cho phép bạn linh hoạt chuyển nhiều loại cùng loại hoặc mặc định sử dụng tên lớp.

// Add to gradle
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"

// Import the extension functions
import path.to.my.kotlin.script.putExtra
import path.to.my.kotlin.script.getEnumExtra

// To Send
intent.putExtra(MyEnumClass.VALUE)

// To Receive
val result = intent.getEnumExtra<MyEnumClass>()

-2

Đừng dùng enum. Lý do # 78 để không sử dụng enums. :) Sử dụng các số nguyên, có thể dễ dàng được từ xa thông qua Gói và Parcelable.


8
@hackbod - 77 lý do khác là gì? ;) Nghiêm túc mà nói - dường như có rất nhiều lợi ích cho enum và chúng thực sự không khó để 'từ xa' - bất kỳ cơ hội nào bạn có thể mở rộng dựa trên lý do của mình chống lại chúng?
Ostergaard

3
@hackbod Xin hãy giải thích. Nếu không nên sử dụng enums thì hãy xóa chúng khỏi API.
dcow

2
Enums là một phần của đặc tả ngôn ngữ Java, vì vậy họ là một chút khó khăn để loại bỏ và vẫn còn có một phù thực hiện Java :)
chút

1
những gì về mEnum.ordinal()? nó trả lại vị trí của phần tử
Saif Hamed

3
Giá trị của Enum.ordinal()được cố định tại thời gian biên dịch. Lần duy nhất nhận xét được áp dụng sẽ chuyển dữ liệu giữa các ứng dụng với các phiên bản khác nhau của enum hoặc trên các bản cập nhật ứng dụng thay đổi thứ tự các thành phần trong enum. Điều đó rất nguy hiểm mỗi khi bạn sử dụng bất cứ thứ gì không phải là nguyên thủy. Để chuyển ý định giữa các hoạt động trong một ứng dụng, Enum.ordinal()nên hoàn toàn an toàn.
Ian McLaird
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.