How to pass an object from one activity to another on Android

后端 未结 30 3795
遇见更好的自我
遇见更好的自我 2020-11-21 04:03

I am trying to work on sending an object of my customer class from one Activity and display it in another Activity.

The code for t

相关标签:
30条回答
  • 2020-11-21 05:06

    I used to set object with Pacelable or Serializable to transfer, but whenever I add other variables to object(model), I have to register it all. It's so nconvenient.

    It's super easy to transfer object between activities or fragments.

    Android DataCache

    0 讨论(0)
  • 2020-11-21 05:08

    You could also write the object's data into temporary Strings and ints, and pass them to the activity. Of course that way, you get the data transported, but not the object itself.

    But if you just want to display them, and not use the object in another method or something like that, it should be enough. I did it the same way to just display data from one object in another activity.

    String fName_temp   = yourObject.getFname();
    String lName_temp   = yourObject.getLname();
    String age_temp     = yourObject.getAge();
    String address_temp = yourObject.getAddress();
    
    Intent i = new Intent(this, ToClass.class);
    i.putExtra("fname", fName_temp);
    i.putExtra("lname", lName_temp);
    i.putExtra("age", age_temp);
    i.putExtra("address", address_temp);
    
    startActivity(i);
    

    You could also pass them in directly instead of the temp ivars, but this way it's clearer, in my opinion. Additionally, you can set the temp ivars to null so that they get cleaned by the GarbageCollector sooner.

    Good luck!

    On a side note: override toString() instead of writing your own print method.

    As mentioned in the comments below, this is how you get your data back in another activity:

    String fName = getIntent().getExtras().getInt("fname");
    
    0 讨论(0)
  • 2020-11-21 05:09

    I found a simple & elegant method:

    • NO Parcelable
    • NO Serializable
    • NO Static Field
    • No Event Bus

    Method 1

    Code for the first activity:

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

    Code for the second activity:

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

    you will find objSent & objReceived have the same hashCode, so they are identical.

    But why can we pass a java object in this way?

    Actually, android binder will create global JNI reference for java object and release this global JNI reference when there are no reference for this java object. binder will save this global JNI reference in the Binder object.

    *CAUTION: this method ONLY work unless the two activities run in the same process, otherwise throw ClassCastException at (ObjectWrapperForBinder)getIntent().getExtras().getBinder("object_value") *

    class ObjectWrapperForBinder defination

    public class ObjectWrapperForBinder extends Binder {
    
        private final Object mData;
    
        public ObjectWrapperForBinder(Object data) {
            mData = data;
        }
    
        public Object getData() {
            return mData;
        }
    }
    

    Method 2

    • for the sender,
      1. use custom native method to add your java object to JNI global reference table(via JNIEnv::NewGlobalRef)
      2. put the return integer (actually, JNIEnv::NewGlobalRef return jobject, which is a pointer, we can cast it to int safely) to your Intent(via Intent::putExtra)
    • for the receiver
      1. get integer from Intent(via Intent::getInt)
      2. use custom native method to restore your java object from JNI global reference table (via JNIEnv::NewLocalRef)
      3. remove item from JNI global reference table(via JNIEnv::DeleteGlobalRef),

    But Method 2 has a little but serious issue, if the receiver fail to restore the java object (for example, some exception happen before restore the java object, or the receiver Activity does not exist at all), then the java object will become an orphan or memory leak, Method 1 don't have this issue, because android binder will handle this exception

    Method 3

    To invoke the java object remotely, we will create a data contract/interface to describe the java object, we will use the aidl file

    IDataContract.aidl

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

    Code for the first activity

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

    Code for the second activity:

    change the android:process attribute in AndroidManifest.xml to a non-empty process name to make sure the second activity run in another 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();
        }
    

    In this way, we can pass an interface between two activities even though they run in different process, and call the interface method remotely

    Method 4

    method 3 seem not simple enough because we must implement an aidl interface. If you just want to do simple task and the method return value is unnecessary, we can use android.os.Messenger

    Code for the first activity( sender):

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

    Code for the second activity ( receiver ):

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

    All the Messenger.send will execute in a Handler asynchronously and sequentially.

    Actually, android.os.Messenger is also an aidl interface, if you have the android source code, you can find a file named IMessenger.aidl

    package android.os;
    
    import android.os.Message;
    
    /** @hide */
    oneway interface IMessenger {
        void send(in Message msg);
    }
    
    0 讨论(0)
  • 2020-11-21 05:09

    Android Activity objects can be destroyed and reconstituted. So, you will need to use another approach to look them - or any object they create!!! - up. That is, you could pass as static class reference but then the object handle (Java calls these "references", as does SmallTalk; but they are not references in the sense of C or assembly) will be possibly invalid later because a "feature" of Android OE is any Activity can be annihilated and reconstituted later.

    The original question asked "How to pass object from one activity to another in Android" and nobody has answered that. For sure, you can serialized (Serializable, Parcelable, to/from JSON) and pass a copy of the object's data and a new object having the same data could be created; but it will NOT have the same references/handles. Also, many others mentioned you can store the reference in a static store. And that will work unless Android decides to onDestroy your Activity.

    So, to really solve the original question you would need a static lookup plus each object will update its reference when/if it is recreated. E.g. each Android Activity would relist itself if its onCreate is called. You can also see how some people use the task list to search out an Activity by name. (system is temporarily destroying this instance of the activity to save space..getRunningTasks, the task list is effectively a specialized listing of the most recent object instance of each Activity).

    For reference:

    Stopped: "The activity is completely obscured by another activity (the activity is now in the "background"). A stopped activity is also still alive (the Activity object is retained in memory, it maintains all state and member information, but is not attached to the window manager). However, it is no longer visible to the user and it can be killed by the system when memory is needed elsewhere."

    onDestroy "system is temporarily destroying this instance of the activity to save space."

    So, the Message Bus is a workable solution. It basically "punts". Rather than try to have references to objects; then you re-architect your design to use MessagePassing instead of SequentialCode. Exponentially harder to debug; but it lets you ignore these sort of OperatingEnvironment understandings. Effectively, each object method access is inverted so the caller posts a Message and the object itself defines a handler for that message. Lots more code but can make it robust with the Android OE restrictions.

    If all you want is the top Activity (typical thing in Android apps due to "Context" being needed everywhere), then you can just have each Activity lists itself as "top" in the static global space whenever its onResume is called. Then your AlertDialog or whatever which needs a context can just grab it from there. Also, its a bit yucky to use a global but can simplifying passing a Context up and down everywhere and, for sure, when you use a MessageBus then IT IS global anyways.

    0 讨论(0)
  • 2020-11-21 05:10

    One option could be letting your custom class implement the Serializable interface and then you can pass object instances in the intent extra using the putExtra(Serializable..) variant of the Intent#putExtra() method.

    Pseudocode:

    //To pass:
    intent.putExtra("MyClass", obj);
    
    // To retrieve object in second Activity
    getIntent().getSerializableExtra("MyClass");
    

    Note: Make sure each nested class of your main custom class has implemented Serializable interface to avoid any serialization exceptions. For example:

    class MainClass implements Serializable {
    
        public MainClass() {}
    
        public static class ChildClass implements Serializable {
    
            public ChildClass() {}
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:10

    We can pass the object from one activity to another activity:

    SupplierDetails poSuppliersDetails = new SupplierDetails();
    

    Inside poSuppliersDetails we have some values. Now I am sending this object to target activity:

    Intent iPODetails = new Intent(ActivityOne.this, ActivityTwo.class);
    iPODetails.putExtra("poSuppliersDetails", poSuppliersDetails);
    

    How to get this in ACtivityTwo:

    private SupplierDetails supplierDetails;
        supplierDetails =(SupplierDetails) getIntent().getSerializableExtra("poSuppliersDetails");
    
    0 讨论(0)
提交回复
热议问题