我正在尝试从一个“活动”发送客户类的对象,并在另一个“”中显示它。

客户类别的代码:

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

我想将其对象从一个“活动”发送到另一个“,然后在另一个活动”上显示数据。

我怎样才能做到这一点?


当前回答

Android活动对象可以被销毁和重建。因此,您需要使用另一种方法来查看它们或它们创建的任何对象!!!-向上的也就是说,您可以作为静态类引用传递,但对象句柄(Java调用这些“引用”,SmallTalk也是如此;但它们不是C或汇编意义上的引用)稍后可能会无效,因为Android OE的一个“特性”是任何活动都可以在稍后被销毁和重新构建。

最初的问题是“如何在Android中将对象从一个活动传递到另一个活动”,但没有人回答这个问题。当然,您可以序列化(Serializable、Parcelable、to/from JSON)并传递对象数据的副本,这样就可以创建具有相同数据的新对象;但它将不具有相同的引用/句柄。此外,许多其他人提到可以将引用存储在静态存储中。除非Android决定开启销毁您的活动,否则这将起作用。

因此,要真正解决原始问题,您需要一个静态查找,再加上每个对象将在重新创建时更新其引用。例如,如果调用其onCreate,则每个Android活动将重新列出自己。您还可以看到一些人如何使用任务列表按名称搜索“活动”。(系统正在临时销毁此活动实例以节省空间。getRunningTasks,任务列表实际上是每个活动的最新对象实例的专门列表)。

供参考:

停止:该活动被另一个活动完全遮挡(该活动现在位于“后台”)。已停止的活动也仍然活动(活动对象保留在内存中,它维护所有状态和成员信息,但未附加到窗口管理器)。但是,用户不再能看到它,当其他地方需要内存时,系统可能会将其关闭销毁“系统正在临时销毁此活动实例以节省空间。”

因此,消息总线是一个可行的解决方案。它基本上是“双关语”。而不是试图引用对象;然后重新构建设计,使用MessagePassing而不是SequentialCode。调试难度成倍增加;但它让你忽略了这些对操作环境的理解。实际上,每个对象方法访问都是反向的,因此调用者发布一条消息,而对象本身定义了该消息的处理程序。更多的代码,但可以使其在Android OE限制下更加健壮。

如果你想要的只是顶部的“活动”(由于到处都需要“上下文”,所以在Android应用程序中很常见),那么只要调用onResume,你就可以让每个“活动”在静态全局空间中将自己列为“顶部”。然后,AlertDialog或任何需要上下文的东西都可以从那里获取它。此外,使用全局有点讨厌,但可以简化到处上下传递上下文,当然,当您使用MessageBus时,IT无论如何都是全局的。

其他回答

使用全局静态变量不是好的软件工程实践。将对象的字段转换为原始数据类型可能是一项繁忙的工作。使用可串行化是可以的,但在Android平台上性能不高。Parcelable是专门为Android设计的,您应该使用它

您可以使用此站点为您的类生成Parcelable代码。

我找到了一个简单优雅的方法:

不可分割无可串行化无静态场无事件总线

方法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对象呢?

实际上,android binder将为java对象创建全局JNI引用,并在没有该java对象的引用时释放该全局JNI参考。binder将在binder对象中保存此全局JNI引用。

*注意:除非两个活动在同一进程中运行,否则此方法将在(ObjectWrapperForBinder)getIntent().getExtras().get-Binder(“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

对于发送者,使用自定义本地方法将java对象添加到JNI全局引用表(通过JNIEnv::NewGlobalRef)将返回整数(实际上,JNIEnv::NewGlobalRef返回jobject,这是一个指针,我们可以将其安全地转换为int)放入Intent(通过Intent::putExtra)用于接收器从Intent获取整数(通过Intent::getInt)使用自定义本机方法从JNI全局引用表还原java对象(通过JNIEnv::NewLocalRef)从JNI全局引用表中删除项(通过JNIEnv::DeleteGlobalRef),

但是方法2有一个很小但很严重的问题,如果接收器无法还原java对象(例如,在还原java对象之前发生了一些异常,或者接收器Activity根本不存在),那么java对象将成为孤立对象或内存泄漏,方法1没有此问题,因为android binder将处理此异常

方法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似乎不够简单,因为我们必须实现aidl接口。如果您只想执行简单的任务,而方法返回值是不必要的,我们可以使用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都将以异步和顺序的方式在处理程序中执行。

实际上,android.os.Messenger也是一个aidl接口,如果你有android源代码,你可以找到一个名为IMessenger.aidl的文件

package android.os;

import android.os.Message;

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

最好的方法是在应用程序中有一个类(称为Control),该类将保存类型为“Customer”的静态变量(在您的情况下)。初始化活动A中的变量。

例如:

Control.Customer = CustomerClass;

然后转到Activity B并从Control类获取它。使用变量后不要忘记赋值null,否则会浪费内存。

大家好,我看到了很多好的选项,但我想知道为什么没有使用绑定?

对我来说,传递对对象的引用似乎比序列化和反序列化对象更有效,但我还没有深入了解这是否是幕后发生的事情。

创建活页夹非常简单。。。

public class MyBinder extends Binder {

    private Object myObject;

    public MyBinder(Object object) {
        myObject = object;
    }

    public Object getObject() {
        return myObject;
    }

}

创造出可以使用它的parcelable并不是那么糟糕的乙醚。

public class MyParcelable implements Parcelable {

    private Object myObject;

    public MyParcelable() {
    }

    public MyParcelable(Parcel parcel) {
        myObject = ((MyBinder)parcel.readStrongBinder()).getObject();
    }

    public void setObject(Object object) {
        myObject = object;
    }

    public Object getObject() {
        return myObject;
    }

    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeStrongBinder(new MyBinder(myObject));
    }

    public int describeContents() {
        return myObject == null ? 0 : 1;
    }

    public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() {

        public MyParcelable createFromParcel(Parcel parcel) {
            return new MyParcelable(parcel);
        }

        public MyParcelable[] newArray(int length) {
            return new MyParcelable[length];
        }

    };
}

这个逻辑非常酷,因为实际上是在将引用从一个活动传递到另一个活动。

我建议检查空值,如果Binder的实例是MyBinder!

为了实现这一点,你。。。

把它送出去

Object myObject = "some object";
MyParcelable myParcelable = new MyParcelable();
myParcelable.setObject(myObject);

intent.putExtra("MyParcelable", myParcelable);

把它拿回来

myParcelable = (MyParcelable) getIntent().getExtras().getParcelable("MyParcelable");
myObject = myParcelable.getObject();

见鬼,有人会变得疯狂,让这个笨蛋成为真正的普通人。

您还可以将对象的数据写入临时字符串和int,并将它们传递给活动。当然,这样可以得到传输的数据,但不能得到对象本身。

但是,如果您只想显示它们,而不在其他方法或类似方法中使用对象,那么就足够了。我以同样的方式在另一个活动中显示一个对象的数据。

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

你也可以直接传递它们,而不是传递临时变量,但在我看来,这样更清楚。此外,您可以将temp ivars设置为null,以便GarbageCollector更快地清理它们。

祝你好运

附带说明:重写toString()而不是编写自己的打印方法。

正如下面的评论中提到的,这是您在另一个活动中获取数据的方式:

String fName = getIntent().getExtras().getInt("fname");