의도를 통해 열거 또는 객체 전달 (최상의 솔루션)


221

시작했을 때 두 개의 다른 ArrayList에 액세스 해야하는 활동이 있습니다. 두 목록 모두 내가 만든 다른 객체입니다.

기본적으로 이러한 객체를 인 텐트에서 액티비티로 전달하는 방법이 필요합니다. addExtras ()를 사용할 수 있지만 Parceable 호환 클래스가 필요합니다. 클래스를 직렬화 가능하게 만들 수는 있지만 프로그램 속도가 느려집니다.

내 옵션은 무엇입니까?

열거 형을 전달할 수 있습니까?

제쳐두고 : 인 텐트에서 매개 변수를 Activity Constructor에 전달하는 방법이 있습니까?


어쩌면 내가 누락 된 것이지만 열거 형은 ArrayList와 어떻게 관련되어 있습니까?
Martin Konecny

답변:


558

이것은 오래된 질문이지만 Enum이 실제로 Serializable있으므로 인 텐트에 추가로 완벽하게 추가 될 수 있다고 언급하지 않습니다 . 이처럼 :

public enum AwesomeEnum {
  SOMETHING, OTHER;
}

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

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

정적 또는 응용 프로그램 전체 변수를 사용하라는 제안은 실제로 나쁜 생각입니다. 이것은 실제로 활동을 상태 관리 시스템에 연결하며 유지 관리, 디버그 및 문제 바인딩이 어렵습니다.


대안 :

좋은 점으로 지적되었다 tedzyc 가 제공하는 솔루션이라는 사실에 대한 Oderik가 당신에게 오류를 제공합니다. 그러나 제안 된 대안은 사용하기가 다소 번거 롭습니다 (제네릭을 사용하더라도).

인 텐트에 열거 형을 추가하는 성능에 대해 정말로 걱정한다면 대신 다음 대안을 제안합니다.

옵션 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)];
  }
}

용법:

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

옵션 2 : (일반적이고 재사용 가능하며 열거 형에서 분리됨)

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);
    }
}

용법:

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

옵션 3 (Kotlin 사용) :

오랜 시간이 지났지 만 지금은 Kotlin을 보유하고 있으므로 새로운 패러다임에 다른 옵션을 추가 할 것이라고 생각했습니다. 여기서는 확장 함수와 구체화 된 형식 (컴파일 할 때 형식을 유지)을 사용할 수 있습니다.

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] }

이렇게하면 몇 가지 이점이 있습니다.

  • inline함수 내부의 코드로 호출을 대체하는 덕분에 직렬화를 수행하기 위해 중개 오브젝트의 "오버 헤드"가 필요하지 않습니다 .
  • 이 기능은 SDK와 유사하므로 더 친숙합니다.
  • IDE는 이러한 기능을 자동 완성하므로 유틸리티 클래스에 대한 사전 지식이 필요하지 않습니다.

단점 중 하나는 Emum의 순서를 변경하면 이전 참조가 작동하지 않는다는 것입니다. 이것은 보류중인 인 텐트 내의 인 텐트와 같은 문제가 업데이트에서 살아남을 수 있으므로 문제가 될 수 있습니다. 그러나 나머지 시간에는 괜찮습니다.

위치 대신 이름을 사용하는 것과 같은 다른 솔루션도 값의 이름을 바꾸면 실패한다는 점에 유의해야합니다. 이러한 경우 잘못된 Enum 값 대신 예외가 발생합니다.

용법:

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

14
응용 프로그램을 광범위하게 만드는 것이 "실제로 나쁜 생각"임을 지적한 +1.
bugfixr

3
실제로 직렬화 또는 번들링 객체 (많은 변수가있는 많은 객체)를 처리하고 싶지 않은 프로젝트에서 작업했으며 정적 전역 변수를 사용하는 것이 좋았습니다 ... 팀원이 계획. 이러한 전역의 사용을 조정하는 데 드는 비용으로 인해 "나사를 만들었습니다. 코드 생성기를 작성하여 Parcelables로 만들었습니다". 버그 수는 크게
줄었

2
@Coeffect 예, 이해할 수있는 제안이지만 대부분의 경우 수천 개의 열거 형을 구문 분석하지 않는 한 조기 최적화로 자격이 될 수 있습니다 (자연적으로 상태를 처리하는 데 사용되는 경우 몇 개만 필요합니다) Nexus 4 당신은 1ms 개선 ( developerphil.com/parcelable-vs-serializable ) 을 얻 습니다. 여분의 레그 작업이 가치가 있는지 확실하지 않지만 다시 제안한 다른 대안이 있습니다.)
pablisco

1
@rgv Kotlin은 enum class형식을 일반 Java로 크로스 컴파일합니다 enum. 나는 쉽게 해결 방법은이 만드는 것입니다 생각 enum class구현 Serializable: enum class AwesomeEnum : Serializable { A, B, C }적합하지 않습니다,하지만 작동합니다.
pablisco

1
@Pierre는 좋은 대답과 마찬가지로 "의존적"입니다. 직렬화 가능을 확장 할 필요가 없습니다. 초기 답변이 유효합니다. 이러한 추가 옵션은 수백만 건의 레코드를 직렬화 해제하는 경우와 같이 병목이있는 경우가 있습니다 (희망하지 않음). 당신이 적합하다고 생각하는 것을 사용하십시오.
pablisco

114

열거 형이 Parcelable을 구현하도록 만들 수 있습니다.

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];
        }
    };
}

그런 다음 Intent.putExtra (String, Parcelable)를 사용할 수 있습니다.

업데이트 : enum.values()매 호출마다 새로운 배열 을 할당하는 wreckgar의 의견에 유의 하십시오.

업데이트 : Android Studio에는 ParcelableEnum이 솔루션을 구현 하는 라이브 템플릿 이 있습니다. (Windows에서는 Ctrl+ 사용 J)


3
서수 대신 열거 형의 toString () 및 valueOf () 메소드도 가능합니다.
Natix

2
개발자가 새 열거 형 멤버를 삽입 할 때 ordinal () 사용이 중단 될 수 있습니다. 물론 열거 형 멤버의 이름을 바꾸면 name ()도 중단됩니다. 그러나 이름을 바꾸려면 전체 프로젝트를 리팩터링해야하므로 개발자는 이름을 바꾸는 대신 새 멤버를 삽입 할 가능성이 높습니다.
Cheok Yan Cheng

2
추가 (또는 재정렬 된) 열거 형 값이 이름이 변경된 것보다 더 동의한다는 데 동의하지 않습니다. IntelliJ IDEA 리팩토링과 같은 정교한 IDE를 사용하는 것은 큰 문제가 아닙니다. 그러나 요점은 여전히 ​​유용합니다. 공존 구현 전체에서 직렬화가 일관되도록해야합니다. 그것은 모든 종류의 직렬화에 해당됩니다. 필자는 대부분의 경우 소포가 하나의 구현 만 존재하는 하나의 앱에서 전달되므로 문제가되지 않아야한다고 가정합니다.
Oderik

2
values ​​()은 모든 호출에서 새로운 배열을 생성하므로 캐시하는 것이 가장 좋습니다. 개인 정적 배열
wreckgar23

2
일반적인 경우 (예 : db 스토리지), ordinal ()이 안전하지 않다는 사실은 Android 소포와 관련이 없습니다. 소포는 장기 보관 용이 아닙니다. 그들은 앱으로 죽습니다. 따라서 열거 형을 추가 / 이름을 바꾸면 새로운 소포가 생깁니다.
noamtm 2016 년

24

열거 형을 문자열로 전달할 수 있습니다.

public enum CountType {
    ONE,
    TWO,
    THREE
}

private CountType count;
count = ONE;

String countString = count.name();

CountType countToo = CountType.valueOf(countString);

주어진 문자열이 지원되면 아무런 문제없이 열거 형 값을 전달할 수 있어야합니다.


3
그들 모두의 가장 간단한 구현.
Avi Cohen

22

열거 형을 의도적으로 전달하려면 열거 형을 정수로 변환 할 수 있습니다.

전의:

public enum Num{A ,B}

전송 (열에서 정수로) :

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

수신 (정수에서 열거로) :

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

친애하는. :)


8
또는 Num.A.name ()을 사용하여 문자열로 읽을 수 있으므로 Num.ValueOf (intent.getStringExtra ( "TEST"))를 사용하여 다시 가져올 수 있습니다.
Benoit Jadinon

1
ordinal () 값이 변경 될 수 있기 때문에 temp.ordinal ()이 실제로 선호되지 않기 때문에 Benoit의 방법이 더 안전하다고 생각합니다. 이 게시물 참조 : stackoverflow.com/questions/2836256/…
Shnkc

15

실제로 필요한 경우 다음과 같이 name()and를 사용하여 열거 형을 문자열로 직렬화 할 수 valueOf(String)있습니다.

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

열거 형에 변경 가능한 상태 (실제로해서는 안되는)가있는 경우 분명히 작동하지 않습니다.


4

Enum을 Serializable로 구현하면 Serializable로 전달하는 방법이 있으므로 Intent를 통해 전달할 수 있습니다. 열거 형 대신 int를 사용하는 조언은 가짜입니다. 열거 형은 코드를보다 쉽게 ​​읽고 유지 관리하기 위해 사용됩니다. Enum을 사용할 수 없도록 어두운 시대로 거슬러 올라갑니다.


2
모든 Enum 유형은 기본적으로 이미 Serializable을 구현하는 Enum 수퍼 클래스를 확장합니다.
Arcao

2

Oderik의 게시물에 대해 :

열거 형이 Parcelable을 구현하도록 만들 수 있습니다.

public enum MyEnum은 Parcelable {...}을 구현합니다. Intent.putExtra (String, Parcelable)을 사용할 수 있습니다.

MyEnum 변수 myEnum을 정의한 경우 intent.putExtra ( "Parcelable1", myEnum)을 수행하면 "인스턴트 유형에 대해 putExtra (String, Parcelable) 메소드가 모호합니다"라는 오류 메시지가 표시됩니다. Intent.putExtra (String, Parcelable) 메소드도 있고 원래 'Enum'유형 자체가 Serializable 인터페이스를 구현하므로 컴파일러는 어떤 메소드 (intent.putExtra (String, Parcelable / or Serializable))를 선택하지 않습니다.

MyEnum에서 Parcelable 인터페이스를 제거하고 핵심 코드를 다음과 같이 랩 클래스의 Parcelable 구현으로 이동하도록 제안하십시오 (Father2는 Parcelable이며 열거 형 필드를 포함 함).

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());
}

}

우리는 할 수 있습니다 :

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

5
예를 들어 다음과 같이 인수를 "주조"하여 정확한 서명을 명시 적으로 선택할 수 있습니다.intent.putExtra("myEnum", (Parcelable) enumValue);
Oderik

서수를 사용하는 것이 완벽합니다!
slott

이것은 정말 복잡한 표현입니다 bundle.putExtra("key", AnotherEnum.X.ordinal()).
TWiStErRob

2

enum에 enum 생성자를 사용하여 기본 데이터 유형을 가질 수 있습니다.

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);
    }
}

int를 엑스트라로 전달한 다음 값을 사용하여 열거 형에서 가져 오는 데 사용할 수 있습니다.


2

여기에서 Parcelable 개념을 사용하는 대부분의 답변은 Java 코드입니다. 코 틀린에서하는 것이 더 쉽습니다.

@Parcelize로 enum 클래스에 주석을 달고 Parcelable 인터페이스를 구현하십시오.

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

1

나는 단순하다.

  • 프레드 활동은 두 가지 모드가 있습니다 - HAPPYSAD.
  • IntentFactory당신을 Intent위해 정적 을 만듭니다 . 는 IT를 통과 Mode당신이 원하는.
  • IntentFactory의 이름을 사용하는 Mode여분의 이름으로 클래스를.
  • IntentFactory변환 ModeA와 String사용name()
  • onCreate이 정보를 입력하면 로 다시 변환됩니다 Mode.
  • 당신은 사용할 수 있습니다 ordinal()Mode.values()뿐만 아니라. 디버거에서 문자열을 볼 수 있기 때문에 문자열을 좋아합니다.

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

궁금합니다. 누가 IntentFactory를 호출합니까? 다른 활동이 Fred에게 전화하는 방법과 Fred가 모드가 통과되도록 보장하는 방법에 대해 자세히 설명 할 수 있습니까?
erik

0

가장 좋은 방법은 해당 목록을 문자열 (또는 맵?)과 같은 소포로 변환하여 활동에 가져 오는 것입니다. 그런 다음 활동은 다시 배열로 변환해야합니다.

맞춤형 소포를 구현하는 것은 목 IMHO의 고통이므로 가능하면 피할 것입니다.


0

다음을 고려하십시오 enum ::

public static  enum MyEnum {
    ValueA,
    ValueB
}

합격의 경우 ::

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

의도 / 번들 / 인수에서 다시 검색하려면 ::

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

0

열거 형을 보내려면 다음과 같이 할 수 있습니다.

먼저 일부 값을 포함하는 열거 형을 선언하십시오 (의도를 통과 할 수 있음).

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

그때:

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

그리고 당신이 그것을 원할 때 :

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

케이크 조각!


0

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] }

이를 통해 동일한 열거 형 유형을 여러 개 전달하거나 클래스 이름을 기본값으로 사용할 수 있습니다.

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

열거 형을 사용하지 마십시오. 열거 형을 사용하지 않는 이유 # 78. :) Bundle 및 Parcelable을 통해 쉽게 원격화할 수있는 정수를 사용하십시오.


8
@hackbod-다른 77 가지 이유는 무엇입니까? ;) 진지하게-열거하는 데 많은 이점이있는 것처럼 보이며 정확하게 '원격'시키는 것이 어렵지 않습니다.
ostergaard

3
@hackbod 정교하게 작성하십시오. 열거 형을 사용하지 않아야하는 경우 API에서 제거하십시오.
dcow

2
열거 형은 Java 언어 사양의 일부이므로 제거하기가 다소 어려우며 호환되는 Java 구현이 있습니다.)
tad

1
무엇에 대해 mEnum.ordinal()? 그것은 요소의 위치 반환의
사이프 하메드

3
의 값은 Enum.ordinal()컴파일 타임에 고정됩니다. 댓글 사이에 데이터를 전달 할 것입니다 적용되는 유일한 시간 애플 리케이션 열거의 다른 버전으로, 또는 열거 내의 요소의 순서를 변경 앱 업데이트를 통해. 그런 종류의 것은 원시가 아닌 것을 사용할 때마다 위험합니다. 단일 앱 내에서 활동간에 의도를 전달하려면 Enum.ordinal()완전히 안전해야합니다.
Ian McLaird
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.