Android에서 한 활동에서 다른 활동으로 오브젝트를 전달하는 방법

고객 클래스 의 객체 를 하나 에서 보내고 Activity다른 것으로 표시 하려고 노력하고 있습니다 Activity.

고객 클래스의 코드 :

public class Customer {

    private String firstName, lastName, Address;
    int Age;

    public Customer(String fname, String lname, int age, String address) {

        firstName = fname;
        lastName = lname;
        Age = age;
        Address = address;
    }

    public String printValues() {

        String data = null;

        data = "First Name :" + firstName + " Last Name :" + lastName
        + " Age : " + Age + " Address : " + Address;

        return data;
    }
}

객체 Activity를 다른 객체로 보내고 다른 데이터를 표시하고 싶습니다 Activity.

어떻게하면 되나요?



답변

하나의 옵션은 사용자 정의 클래스가 Serializable인터페이스를 구현하게 한 다음 메소드 의 putExtra(Serializable..)변형을 사용하여 추가 목적으로 객체 인스턴스를 전달할 수 있습니다 Intent#putExtra().

의사 코드 :

//To pass:
intent.putExtra("MyClass", obj);

// To retrieve object in second Activity
getIntent().getSerializableExtra("MyClass");

참고 : 기본 사용자 정의 클래스의 각 중첩 클래스가 직렬화 예외를 피하기 위해 직렬화 가능 인터페이스를 구현했는지 확인하십시오. 예를 들면 다음과 같습니다.

class MainClass implements Serializable {

    public MainClass() {}

    public static class ChildClass implements Serializable {

        public ChildClass() {}
    }
}


답변

Serializable을 사용하여 클래스를 구현하십시오. 이것이 엔터티 클래스라고 가정 해 봅시다.

import java.io.Serializable;

@SuppressWarnings("serial") //With this annotation we are going to hide compiler warnings
public class Deneme implements Serializable {

    public Deneme(double id, String name) {
        this.id = id;
        this.name = name;
    }

    public double getId() {
        return id;
    }

    public void setId(double id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    private double id;
    private String name;
}

deneX 액티비티에서 Y 액티비티로 호출 된 객체를 보냅니다 . X 활동의 어딘가;

Deneme dene = new Deneme(4,"Mustafa");
Intent i = new Intent(this, Y.class);
i.putExtra("sampleObject", dene);
startActivity(i);

Y 활동에서 우리는 객체를 얻고 있습니다.

Intent i = getIntent();
Deneme dene = (Deneme)i.getSerializableExtra("sampleObject");

그게 다야.


답변

  • 전역 정적 변수를 사용하는 것은 좋은 소프트웨어 엔지니어링 관행 이 아닙니다 .
  • 객체의 필드를 원시 데이터 유형 으로 변환하는 것은 바쁜 작업이 될 수 있습니다 .
  • 직렬화 가능을 사용하는 것은 좋지만 Android 플랫폼 에서는 성능 비효율적 입니다.
  • Parcelable은 Android 용 으로 특별히 설계되었으며 사용해야합니다. 간단한 예제는 다음과 같습니다. Android 활동간에 사용자 정의 오브젝트 전달

사이트를 사용하여 클래스에 대한 Parcelable 코드를 생성 할 수 있습니다 .


답변

gson 을 사용 하여 객체를 JSON으로 변환하고 의도를 전달하십시오. 새로운 활동에서 JSON을 객체로 변환하십시오.

에서 build.gradle의존성에 추가하십시오.

implementation 'com.google.code.gson:gson:2.8.4'

활동에서 객체를 json-string으로 변환하십시오.

Gson gson = new Gson();
String myJson = gson.toJson(vp);
intent.putExtra("myjson", myjson);

수신 활동에서 json-string을 원래 객체로 다시 변환하십시오.

Gson gson = new Gson();
YourObject ob = gson.fromJson(getIntent().getStringExtra("myjson"), YourObject.class);

들어 코 틀린 꽤 동일합니다

데이터를 전달

val gson = Gson()
val intent = Intent(this, YourActivity::class.java)
intent.putExtra("identifier", gson.toJson(your_object))
startActivity(intent)

데이터 수신

val gson = Gson()
val yourObject = gson.fromJson<YourObject>(intent.getStringExtra("identifier"), YourObject::class.java)


답변

활동을 부르는 동안

Intent intent = new Intent(fromClass.this,toClass.class).putExtra("myCustomerObj",customerObj);

toClass.java에서 활동을 수신하십시오.

Customer customerObjInToClass = getIntent().getExtras().getParcelable("myCustomerObj");

고객 클래스가 소포를 구현하는지 확인하십시오

public class Customer implements Parcelable {

    private String firstName, lastName, address;
    int age;

    /* all your getter and setter methods */

    public Customer(Parcel in ) {
        readFromParcel( in );
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public LeadData createFromParcel(Parcel in ) {
            return new Customer( in );
        }

        public Customer[] newArray(int size) {
            return new Customer[size];
        }
    };


    @Override
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeString(firstName);
        dest.writeString(lastName);
        dest.writeString(address);
        dest.writeInt(age);
    }

    private void readFromParcel(Parcel in ) {

        firstName = in .readString();
        lastName  = in .readString();
        address   = in .readString();
        age       = in .readInt();
    }


답변

내 경험에는 각각 단점과 장점이있는 세 가지 주요 솔루션이 있습니다.

  1. 소포 가능 구현

  2. 직렬화 가능 구현

  3. 일종의 경량 이벤트 버스 라이브러리 사용 (예 : Greenrobot의 EventBus 또는 Square ‘s Otto)

소포 가능 -빠르며 안드로이드 표준이지만 보일러 플레이트 코드가 많으며 의도를 강하게 입력 할 때 참조하기 위해 하드 코딩 된 문자열이 필요합니다 (강하지 않은 유형).

직렬화 가능 -상용구가 0에 가깝지만 가장 느리게 접근하는 방식이며 값을 의도하지 않은 값으로 가져올 때 하드 코딩 된 문자열이 필요합니다 (비 유형).

이벤트 버스 -상용구 없음, 가장 빠른 접근 방식, 하드 코딩 된 문자열이 필요하지 않지만 추가 종속성이 필요함 (보통 약 40KB 임)

효율성 벤치 마크를 포함하여이 세 가지 접근 방식을 매우 상세하게 비교했습니다.


답변

간단하고 우아한 방법을 찾았습니다.

  • 소포 없음
  • 직렬화 불가능
  • 정적 필드 없음
  • 이벤트 버스 없음

방법 1

첫 번째 활동을위한 코드 :

    final Object objSent = new Object();
    final Bundle bundle = new Bundle();
    bundle.putBinder("object_value", new ObjectWrapperForBinder(objSent));
    startActivity(new Intent(this, SecondActivity.class).putExtras(bundle));
    Log.d(TAG, "original object=" + objSent);

두 번째 활동을위한 코드 :

    final Object objReceived = ((ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value")).getData();
    Log.d(TAG, "received object=" + objReceived);

당신은 발견 할 것이다 objSent & objReceived동일이 hashCode가 동일하므로,.

그러나 왜 이런 식으로 Java 객체를 전달할 수 있습니까?

실제로 안드로이드 바인더는 자바 객체에 대한 글로벌 JNI 참조를 생성하고이 자바 객체에 대한 참조가 없을 때이 글로벌 JNI 참조를 해제합니다. 바인더는이 글로벌 JNI 참조를 바인더 오브젝트에 저장합니다.

*주의 :이 메소드는 두 활동이 동일한 프로세스에서 실행되지 않는 한 작동합니다. 그렇지 않으면 (ObjectWrapperForBinder) getIntent (). getExtras (). getBinder ( “object_value”)에서 ClassCastException을 발생시킵니다.

ObjectWrapperForBinder 정의 클래스

public class ObjectWrapperForBinder extends Binder {

    private final Object mData;

    public ObjectWrapperForBinder(Object data) {
        mData = data;
    }

    public Object getData() {
        return mData;
    }
}

방법 2

  • 발신자에게
    1. 사용자 정의 원시 메소드를 사용하여 JNI 글로벌 참조 테이블에 Java 오브젝트를 추가하십시오 (JNIEnv :: NewGlobalRef를 통해)
    2. 반환 정수를 넣습니다 (실제로 JNIEnv :: NewGlobalRef 반환 jobject, 포인터입니다, 안전하게 int로 캐스트 할 수 있습니다) intent (Intent :: putExtra를 통해)
  • 수신기
    1. Intent에서 정수 가져 오기 (Intent :: getInt를 통해)
    2. JNI 글로벌 참조 테이블에서 JNIEnv :: NewLocalRef를 통해 Java 오브젝트를 복원하려면 사용자 정의 고유 메소드 사용
    3. JNI 글로벌 참조 테이블에서 항목 제거 (JNIEnv :: DeleteGlobalRef를 통해),

그러나 방법 2에는 수신자가 java 오브젝트를 복원하지 못하면 (예를 들어, java 오브젝트를 복원하기 전에 일부 예외가 발생하거나 수신자 활동이 전혀 존재하지 않는 경우) 약간의 심각한 문제가 있습니다. 그러면 java 오브젝트는 안드로이드 바인더 가이 예외를 처리하기 때문에 고아 또는 메모리 누수, 방법 1에는이 문제가 없습니다

방법 3

java 객체를 원격으로 호출하기 위해 java 객체를 설명하기위한 데이터 계약 / 인터페이스를 작성하고 aidl 파일을 사용합니다.

IDataContract.aidl

package com.example.objectwrapper;
interface IDataContract {
    int func1(String arg1);
    int func2(String arg1);
}

첫 번째 활동을위한 코드

    final IDataContract objSent = new IDataContract.Stub() {

        @Override
        public int func2(String arg1) throws RemoteException {
            // TODO Auto-generated method stub
            Log.d(TAG, "func2:: arg1=" + arg1);
            return 102;
        }

        @Override
        public int func1(String arg1) throws RemoteException {
            // TODO Auto-generated method stub
            Log.d(TAG, "func1:: arg1=" + arg1);
            return 101;
        }
    };
    final Bundle bundle = new Bundle();
    bundle.putBinder("object_value", objSent.asBinder());
    startActivity(new Intent(this, SecondActivity.class).putExtras(bundle));
    Log.d(TAG, "original object=" + objSent);

두 번째 활동을위한 코드 :

두 번째 활동이 다른 프로세스에서 실행되도록 AndroidManifest.xml의 android : process 속성을 비어 있지 않은 프로세스 이름으로 변경하십시오.

    final IDataContract objReceived = IDataContract.Stub.asInterface(getIntent().getExtras().getBinder("object_value"));
    try {
        Log.d(TAG, "received object=" + objReceived + ", func1()=" + objReceived.func1("test1") + ", func2()=" + objReceived.func2("test2"));
    } catch (RemoteException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

이러한 방식으로 두 프로세스가 서로 다른 프로세스에서 실행 되더라도 두 활동간에 인터페이스를 전달하고 원격으로 인터페이스 메소드를 호출 할 수 있습니다.

방법 4

방법 3은 보조 인터페이스를 구현해야하기 때문에 충분히 단순 해 보이지 않습니다. 간단한 작업을하고 메소드 반환 값이 필요하지 않은 경우 android.os.Messenger를 사용할 수 있습니다

첫 번째 활동 코드 (발신자) :

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity";

    public static final int MSG_OP1 = 1;
    public static final int MSG_OP2 = 2;

    public static final String EXTRA_MESSENGER = "messenger";

    private final Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Log.e(TAG, "handleMessage:: msg=" + msg);
            switch (msg.what) {
            case MSG_OP1:

                break;
            case MSG_OP2:
                break;

            default:

                break;
            }
            super.handleMessage(msg);
        }

    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startActivity(new Intent(this, SecondActivity.class).putExtra(EXTRA_MESSENGER, new Messenger(mHandler)));
    }
}

두 번째 활동에 대한 코드 (수신자) :

public class SecondActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        final Messenger messenger = getIntent().getParcelableExtra(MainActivity.EXTRA_MESSENGER);
        try {
            messenger.send(Message.obtain(null, MainActivity.MSG_OP1, 101, 1001, "10001"));
            messenger.send(Message.obtain(null, MainActivity.MSG_OP2, 102, 1002, "10002"));
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

모든 Messenger.send는 Handler에서 비동기 적으로 순차적으로 실행됩니다.

실제로 android.os.Messenger는 보조 인터페이스입니다 .Android 소스 코드가 있으면 IMessenger.aidl이라는 파일을 찾을 수 있습니다.

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}