Programing

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

lottogame 2020. 5. 8. 08:11
반응형

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


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

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

내 옵션은 무엇입니까?

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

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


이것은 오래된 질문이지만 모두 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.qualifiedName, victim.ordinal)

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

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

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

용법:

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

열거 형이 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)


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

public enum CountType {
    ONE,
    TWO,
    THREE
}

private CountType count;
count = ONE;

String countString = count.name();

CountType countToo = CountType.valueOf(countString);

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


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

전의:

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

친애하는. :)


실제로 필요한 경우 다음과 같이 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;
     }
   }
 }

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


It may be possible to make your Enum implement Serializable then you can pass it via the Intent, as there is a method for passing it as a serializable. The advice to use int instead of enum is bogus. Enums are used to make your code easier to read and easier to maintain. It would a large step backwards into the dark ages to not be able to use Enums.


about Oderik's post:

You can make your enum implement Parcelable which is quite easy for enums:

public enum MyEnum implements Parcelable { ... } You can than use Intent.putExtra(String, Parcelable).

If you define a MyEnum variable myEnum, then do intent.putExtra("Parcelable1", myEnum), you will get a "The method putExtra(String, Parcelable) is ambiguous for the type Intent" error message. because there is also a Intent.putExtra(String, Parcelable) method, and original 'Enum' type itself implements the Serializable interface, so compiler does not know choose which method(intent.putExtra(String, Parcelable/or Serializable)).

Suggest that remove the Parcelable interface from MyEnum, and move the core code into wrap class' Parcelable implementation, like this(Father2 is a Parcelable and contain an enum field):

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

}

then we can do:

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

you can use enum constructor for enum to have primitive data type..

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

you can use to pass int as extras then pull it from enum using its value.


I like simple.

  • The Fred activity has two modes -- HAPPY and SAD.
  • Create a static IntentFactory that creates your Intent for you. Pass it the Mode you want.
  • The IntentFactory uses the name of the Mode class as the name of the extra.
  • The IntentFactory converts the Mode to a String using name()
  • Upon entry into onCreate use this info to convert back to a Mode.
  • You could use ordinal() and Mode.values() as well. I like strings because I can see them in the debugger.

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

I think your best bet is going to be to convert those lists into something parcelable such as a string (or map?) to get it to the Activity. Then the Activity will have to convert it back to an array.

Implementing custom parcelables is a pain in the neck IMHO so I would avoid it if possible.


Consider Following enum ::

public static  enum MyEnum {
    ValueA,
    ValueB
}

For Passing ::

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

To retrieve back from the intent/bundle/arguments ::

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

Don't use enums. Reason #78 to not use enums. :) Use integers, which can easily be remoted through Bundle and Parcelable.

참고URL : https://stackoverflow.com/questions/2836256/passing-enum-or-object-through-an-intent-the-best-solution

반응형