【问题标题】:Parceling objects in android to transfer from one activity to another在android中打包对象以从一个活动转移到另一个活动
【发布时间】:2015-06-10 15:23:48
【问题描述】:

最近一位面试官问了我一个非常棘手的问题。 这个问题有几个部分。

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

我给出的答案 -

Parcelable 使开发人员能够限制对象 在某种程度上使其使用起来更快。

我对此感到困惑,因此决定找出使用可序列化和可打包之间的区别:p(聪明的huuuhhh!), http://www.developerphil.com/parcelable-vs-serializable/ 使用了这个引用。

  1. 在使用Bundle时,当我们使用String、int时,我们不需要对数据进行打包,所以你认为String/int默认是内部打包的吗?

我给出的答案 -

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

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

【问题讨论】:

  • 出于好奇,您面试的工作是什么?我的意思是要创建一个特定的应用程序吗? (这与问题无关,我还在为你想办法)
  • 一个普通的android应用程序开发需求,面试官开始问一些基本的问题,比如如何在活动之间传输数据?最好的方法是什么?然后把问题改成了一个棘手的问题。

标签: android bundle parcelable


【解决方案1】:

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

分割/序列化对象并不像您猜想的那样为了速度。

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

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

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

因此,当您想将对象myObject 发送给某个接收Activity 时,您不能发送它的引用/指针,因为接收者不一定有权访问指针指定的位置。相反,您必须发送一些myObject 的表示,以便接收者可以访问和使用——这就是为什么您需要将数据编组 成可以解组 的形式strong>,最简单的方法是简单地让对象的类实现Serializable,这让 Java 可以尽最大努力将对象转换为可以轻松发送到接收方并由接收方解组的字节数组。但由于Serializable 使用反射,这很慢。

您可以使用其他更快的方式来编组数据——例如,使用Gson 之类的库将对象转换为其JSON 表示形式,然后将其从任何JSON 文档中发送出去可以表示为 String 并轻松转换回 Java Object。另一种可能在几乎所有情况下都更快的方法是使用Parcelable 接口,它允许您指定准确您希望如何编组数据以及准确应该如何编组被解组。它基本上可以让您更好地控制对象的传输。

tl:dr: Parcelling/Serializing etc 被使用是因为你不能发送内存地址,所以你必须发送对象的实际数据并且它必须用一些来表示形式。

在使用Bundle的时候,当我们使用String、int时,我们不需要对数据进行打包,所以你认为String/int默认是内部打包的吗?

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

但是原始类型和字符串足够简单并且经常使用,开发人员不需要指定如何需要发生并且Bundle提供了方便的方法为了它。我无法在它们工作的最低级别上给你一个可靠的答案,因为很多 Parcel 代码是本地实现的,我无法在网上找到它,但它们肯定可以直接转换为它们的表示形式字节。

【讨论】:

    【解决方案2】:

    只是补充一下@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++ 应用程序向 java 应用程序发送数据(Android 类的对象)时,它在 C++ 中的编译和链接方式与在 java 中的编译方式完全不同,因此 java 会想知道这个 C++ 有什么申请已发送给我。

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

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

    第二个问题,

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

    当我们继续深入挖掘时,我尝试检查代码,最终得到了 @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;
    

    设置一个幻数,它将在写入时识别包裹的开始,在我们读取包裹时使用相同的数字。

    希望我回答了你的问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-05
      • 1970-01-01
      • 2018-10-10
      • 2012-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多