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


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

나는 단순하다.

  • 프레드 활동은 두 가지 모드가 있습니다 - HAPPY와 SAD.
  • 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.