在android中分配对象以从一个活动转移到另一个活动

时间:2015-06-10 15:23:49

标签: android bundle parcelable

最近一位采访者问我一个非常棘手的问题。 问题有几个部分。

  1. 为什么(问题是为什么而不是如何)在从一个活动发送到另一个活动而不是直接发送时需要包裹对象
  2. 我给的答案 -

      

    Parcelable为开发人员提供了限制对象的能力   在某种程度上使用它的速度更快。

    我对这部分感到困惑,所以决定使用serializable和parcelable之间的区别:p(聪明的huuuhhh!), http://www.developerphil.com/parcelable-vs-serializable/使用了此引用。

    1. 在使用Bundle时,当我们使用String时,int我们不需要包裹数据,所以你认为String / int默认是在内部包含的吗?
    2. 我给的答案 -

        

      因为String / int是原始数据类型,如果我们使用了   直接包装类,可能我们不得不使用parcelable(我   我不确定那部分)

      谷歌搜索后我没有得到任何有用的链接,我或者面试官对答案也不太满意。 如果你们能提供帮助,那就太好了!

2 个答案:

答案 0 :(得分:6)

  

为什么(问题是为什么而不是如何)在从一个活动发送到另一个活动而不是直接发送时需要包裹对象

Parcelling / serializing对象并不像你猜到的那样速度。

当您在活动之间,特别是在不同的应用程序之间发送数据时(请记住,Intent对象不仅适用于您自己的活动之间的通信,也适用于您和其他应用之间的通信。好吧),您不能指望发送方和接收方可以访问相同的内存地址空间。

Android的文档指出应用程序在自己的离散内存空间中运行。以下是来自文档的引用:

  

每个进程都有自己的虚拟机(VM),因此应用程序的代码与其他应用程序隔离运行。

因此,当您想要将对象myObject发送给某些接收Activity时,您无法发送其引用/指针,因为接收方无法访问指针指定的位置。相反,您必须发送一些接收者可以访问和使用的myObject代表 - 这就是为什么您需要将编组数据转换为可以解组的形式,最简单的方法是简单地使用对象实现Serializable的类,它允许Java尽最大努力将对象转换为可以轻松发送和解组的字节数组。由接收者。但由于Serializable使用反射,因此速度很慢。

您可以使用其他更快速编组数据的方法 - 例如,使用像JSON这样的库将对象转换为Gson表示形式,然后将其发送到任何方式JSON文档可以表示为String,并可以轻松转换回Java Object。另一种方法,在所有情况下可能更快,使用Parcelable接口,您可以指定完全您希望如何编组数据,完全如何它应该是未编组的。它基本上可以让您更好地控制对象的传输。

tl:dr :使用Parcelling / Serializing等因为你不能发送内存地址,所以你必须发送对象的实际数据,它必须在某些地方表示形成。

  

在使用Bundle时,当我们使用String时,int我们不需要包裹数据,所以你认为String / int默认是在内部分配的吗?

Bundle内部的工作原理是它将所有内容放入Map并根据需要包裹/取消数据集(即调用get / put时)。要将对象放入Bundle,对象的类需要实现SerializableParcelable,因为它需要告诉Bundle它应该如何在内部编组/解组。

原始类型和字符串非常简单并经常使用,以至于开发人员不需要指定 需要发生的事情,Bundle提供方便的方法。我无法在最低级别的工作中给出一个可靠的答案,因为很多Parcel代码是本地实现的,我无法在线找到它,但它们必须直接转换为它们的以字节为单位的表示。

答案 1 :(得分:0)

只是要添加@ uj-所说的,需要Parcelling / Serializing,因为@ uj-表示它将通过JVM发送,因此需要将它们转换为某种格式,以便对方能够理解。

让我举一个例子来解释为什么需要序列化/分配,

您正在将用“C ++”编写的应用程序中的数据发送到用java编写的应用程序,因此以下是类,

在C ++中,

class Android {
    public: int dataToSend; //for example purpose making field public and omitting setter/getters
}

在Java中,

class Android{
    public int dataToSend;
}

假设C ++代码生成动态库(将通过使用标准C ++编译器进行编译然后链接生成),Java代码生成jar(通过使用javac进行编译)。

当C ++应用程序将数据(Android类的对象)发送到java应用程序时,它在C ++中编译和链接的方式与它在java中编译的方式完全不同,因此java会想知道这个C ++是什么申请已发送给我。

因此,为了摆脱这些问题,需要序列化/分配,这将确保两个应用程序都知道数据在通过网络传输时如何转换(在android的情况下如何将其传输到另一个活动,可能在相同或不同的应用)。

当我们开始比较Serialization和Parcelling时,Parcelling占了上风,因为我们将在发送数据时指定数据必须转换的方式,否则在序列化的情况下,使用反射将对象转换为字符串反思总是需要时间。因此,与序列化相比,Parcelling更快。

关于第二个问题,

如果我们考虑上面的例子,那么我们可以说String和int是原始类型(其中没有用户定义的字段),因此android将能够处理将要发送的数据的编组和解组。 / p>

当我们继续深入挖掘时,我尝试了解代码,最终得到了@uj所说的本机代码。

一些来自android源代码的摘录: 在写包裹时:

parcel.writeInt(BUNDLE_MAGIC);
int startPos = parcel.dataPosition();
parcel.writeArrayMapInternal(mMap);
int endPos = parcel.dataPosition();

parcel.setDataPosition(lengthPos);
int length = endPos - startPos;
parcel.writeInt(length);
parcel.setDataPosition(endPos);

在阅读包裹时,

int magic = parcel.readInt();
if (magic != BUNDLE_MAGIC) {
    //noinspection ThrowableInstanceNeverThrown
    throw new IllegalStateException("Bad magic number for Bundle: 0x"
    + Integer.toHexString(magic));
}

int offset = parcel.dataPosition();
parcel.setDataPosition(offset + length);

Parcel p = Parcel.obtain();
p.setDataPosition(0);
p.appendFrom(parcel, offset, length);
p.setDataPosition(0);

mParcelledData = p;

设置在写入时识别包裹起点的幻数,并在读取包裹时使用相同的数字。

希望我回答你的问题。