如何通过捆绑发送对象


119

我需要通过一个捆绑包传递对该类进行大部分处理的类的引用。

问题在于它与意图或上下文无关,并且具有大量非原始对象。如何将类打包为可打包/可序列化的类并将其传递给startActivityForResult


2
“我需要通过一个捆绑包传递对我的大部分处理工作的类的引用” –为什么?
CommonsWare,2010年

1
我有一个对象(DataManager),它处理服务器并为某些GUI运行一些后端。建立新连接后,我希望用户能够启动一个新活动,该活动在ListView中列出所有活动的连接并让用户选择一个。然后,结果数据将绑定到新的GUI。这确实只是后端的皮肤选择器。
ahodder

3
如果要通过多个活动处理对象的同一实例,则可能需要考虑singleton模式。有一个很好的教程在这里
sotrh 2014年

Answers:


55

弄清楚走哪条路不仅需要回答CommonsWare的关键问题“为什么”,还需要回答“到什么?”的问题。你在通过吗

现实情况是,唯一可以通过捆绑销售的商品是纯数据-其他所有内容都基于对数据含义或指向的解释。您不能从字面上传递对象,但是您可以做的是以下三件事之一:

1)您可以将对象分解成其构成数据,并且如果另一端对相同类型的对象有所了解,则可以从序列化数据中组装一个副本。这就是大多数常见类型通过包传递的方式。

2)您可以传递不透明的手柄。如果在相同的上下文(尽管可能会问为什么要打扰)中传递它,则可以调用或取消引用它。但是,如果您通过Binder将其传递到其他上下文,则其字面值将是任意数字(实际上,这些任意数字从启动起就按顺序计数)。除了跟踪它之前,您什么也不能做,除非将其传递回原始上下文,这将使Binder将其转换回原始句柄,使其再次有用。

3)您可以传递魔术句柄,例如文件描述符或对某些os / platform对象的引用,如果设置了正确的标记,则Binder将创建一个指向接收者相同资源的克隆,该克隆实际上可用于另一端。但这仅适用于极少数类型的对象。

最有可能的是,您只是传递您的类,以便另一端可以跟踪它并在以后将其返回给您,或者您将其传递给可以从序列化组成数据创建克隆的上下文...否则您正在尝试做一些行不通的事情,您需要重新考虑整个方法。


1
感谢您的旅行回复。您的权利,我需要做的只是将对象列表的引用传递给我的新活动。新活动将从列表中获取一些数据并显示一个可选的ListView。onSelect,活动将向主机活动返回结果(一些与click对象有关的数据)。如果我理解正确,我相信您的选项2会最恰当地处理此问题;我该如何获得不透明的手柄?
2010年

您的其他活动无法从不透明对象中提取任何数据以进行显示。您可能想要做的是创建并传递一些受支持类型的代理对象,这些对象包含将要显示的信息的副本。
克里斯·斯特拉顿

158

您还可以使用Gson将对象转换为JSONObject并将其传递给bundle。对我来说,这是我发现的最优雅的方式。我尚未测试它如何影响性能。

在初始活动中

Intent activity = new Intent(MyActivity.this,NextActivity.class);
activity.putExtra("myObject", new Gson().toJson(myobject));
startActivity(activity);

在下一个活动中

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);

3
由于这是在活动之间传递内容的问题,因此发生的频率并不足以对应用程序的整体性能产生重大影响。话虽这么说,我怀疑是否可以序列化原始帖子的DataManager,因为听起来好像它具有套接字连接和其他类似的类。
britzl

4
Google也建议使用此解决方案,而不要序列化:请查看本文档页面
TechNyquist 2014年

3
就像警告一样,我使用了一段时间,但是对于String可以传递的内容存在内存限制,因此请确保您的数据不会太大。
jiduvah 2015年

请参阅blog.madadipouya.com/2015/09/21/…,以了解如何在项目中添加Gson支持。
geekQ'7

聪明的解决方案,向您致敬!
罗希特·曼迪瓦

20

包裹的接口是通过将物体与意图的好方法。

如何使自定义对象可拆分?是关于如何使用的很好的答案 Parcelable的

谷歌官方文档还包括一个示例


1
或者它们也可以序列化。
Jeffrey Blattman

1
但是会大大降低性能10倍!!查看此基准测试:developerphil.com/parcelable-vs-serializable
saiyancoder 2014年

2
+1 @Mati的注释,但是将其应用于上下文10x时,将其应用于单个对象相当于1 ms。因此,也许听起来并不那么糟糕。
pinoyyid 2014年

1
同意。问题是当您处理集合时,如果您从Rest API获取资源,这是一个非常常见的用例。但是对于单个对象,应该不是臭名昭著的东西。无论如何,如果所有样板代码都是您遇到的麻烦,那么您可以尝试以下可为您生成全部代码的lib:github.com/johncarl81/parceler。一个非常好的方法!
saiyancoder 2014年

断链:404(未找到)
Gallal,2014年

14

您可以使用全局应用程序状态。

更新:

自定义,然后将其添加到您的AndroidManifest.xml中:

<application android:label="@string/app_name" android:debuggable="true" android:name=".CustomApplication"

然后在您的项目中有一个这样的类:

package com.example;

import android.app.Application;

public class CustomApplication extends Application {
    public int someVariable = -1;
}

并且由于“ 可以从任何活动或服务通过getApplication()访​​问它 ”,因此您可以像这样使用它:

CustomApplication application = (CustomApplication)getApplication();
application.someVariable = 123; 

希望有帮助。


1
感谢您的答复,但是如何?
ahodder

我相信您只是将Application子类化,然后可以存储您喜欢的任何东西。上面的链接中提到了您需要的xml更改。
Mark Storer

9
作为一般设计原则,除非您确实需要全局变量,否则最好避免使用全局变量。在这种情况下,有很好的选择。
dhaag23年

好吧,我想我明白你的意思。只需扩展Application并抛出一个保存需要传递的对象的变量;我查看了参考页面,但没有看到必要的xml更改。
2010年

我也想以此作为答案。这绝对是做到这一点的方法之一。但是请记住,除非您取消对它们的引用(否则App上下文会被破坏),否则这些对象将保留在内存中,并且在不需要它们时会占用空间。
IgorČordaš14年


10

可能的解决方案:

Bundle bundle = new Bundle();
bundle.putSerializable("key", new CustomObject());

类CustomObject:

class CustomObject implements Serializable{
 private SubCustomObject1 sc1;
 private SubCustomObject2 sc2;
}

子自定义对象:

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

通过捆绑发送对象的另一种方法是使用bundle.putByteArray
示例代码

public class DataBean implements Serializable {
private Date currentTime;

public setDate() {
    currentTime = Calendar.getInstance().getTime();
 }

public Date getCurrentTime() {
    return currentTime;
 }
}

将DataBean的对象放入Bundle中:

class FirstClass{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//When you want to start new Activity...
Intent dataIntent =new Intent(FirstClass.this, SecondClass.class);
            Bundle dataBundle=new Bundle();
            DataBean dataObj=new DataBean();
            dataObj.setDate();
            try {
                dataBundle.putByteArray("Obj_byte_array", object2Bytes(dataObj));

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }

            dataIntent.putExtras(dataBundle);

            startActivity(dataIntent);
}

将对象转换为字节数组

/**
 * Converting objects to byte arrays
 */
static public byte[] object2Bytes( Object o ) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream( baos );
      oos.writeObject( o );
      return baos.toByteArray();
    }

从Bundle中获取对象:

class SecondClass{
DataBean dataBean;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//Get Info from Bundle...
    Bundle infoBundle=getIntent().getExtras();
    try {
        dataBean = (DataBean)bytes2Object(infoBundle.getByteArray("Obj_byte_array"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

从字节数组获取对象的方法:

/**
 * Converting byte arrays to objects
 */
static public Object bytes2Object( byte raw[] )
        throws IOException, ClassNotFoundException {
      ByteArrayInputStream bais = new ByteArrayInputStream( raw );
      ObjectInputStream ois = new ObjectInputStream( bais );
      Object o = ois.readObject();
      return o;
    }

希望这对其他伙伴有帮助。


这看起来很顺畅,易于查看代码。但是我觉得还有更多关于为什么SDK不提供这样的对象传递对象的信息。您能告诉我有关此解决方案的更多信息吗?
Mario Lenci

3
完全不需要所有代码!使用bundle.putSerializable(objectImplementingSerializable)-这确实在您在此处重新实现的功能下……
Risadinha 2013年

3

1.一个非常直接且易于使用的例子,使对象通过工具Serializable。

class Object implements Serializable{
    String firstName;
   String lastName;
}

2.在捆绑中传递对象

Bundle bundle = new Bundle();
Object Object = new Object();
bundle.putSerializable("object", object);

3.从捆绑中获取传递的对象为可序列化对象,然后转换为对象。

Object object = (Object) getArguments().getSerializable("object");

0

这是对我自己的问题的一个非常迟来的答案,但是它一直在引起关注,所以我觉得我必须解决它。这些答案大多数都是正确的,可以完美地完成工作。但是,这取决于应用程序的需求。该答案将用于描述针对此问题的两种解决方案。

应用

第一个是Application,因为它是这里答案最多的。该应用程序是放置需要引用上下文的实体的好对象。毫无疑问,“ ServerSocket”将需要一个上下文(用于文件I / o或简单的“ ListAdapter”更新)。我个人更喜欢这条路线。我喜欢应用程序,它们对于上下文检索很有用(因为可以将它们设置为静态的并且不太可能导致内存泄漏),并且具有简单的生命周期。

服务

服务`是第二。实际上,对于我的问题,“服务”是更好的选择,因为服务的目的是:
服务是一个应用程序组件,可以在其中执行长时间运行的操作
背景,并且不提供用户界面。
服务是整洁的,因为它们具有更明确的生命周期,更易于控制。此外,如果需要,服务可以在应用程序外部运行(即在启动时)。这对于某些应用程序或仅是一项整洁的功能可能是必需的。

这不是一个完整的描述,但是我为想要进行更多调查的人留下了指向文档的链接。总体而言,Service对于我需要的实例而言,效果更好-在我的SPP设备上运行ServerSocket。


0

我在寻找一种传递Date对象的方法时遇到了这个问题。就我而言,正如答案中所建议的那样,我使用了Bundle.putSerializable(),但这不适用于复杂的事情,如原始文章中所述的DataManager。

我的建议与将所述DataManager放入应用程序或使其成为Singleton的结果非常相似,是使用依赖注入并将DataManager绑定到Singleton范围,并在需要的地方注入DataManager。不仅获得了提高可测试性的好处,而且还获得了更简洁的代码,而没有所有样板代码“在类和活动之间传递依赖关系”代码。(Robo)Guice非常易于使用,新的Dagger框架看起来也很有希望。


1
好吧,使用Date之类的东西,您可以传递long值。但是,其余的听起来不错。谢谢。
ahodder 2013年

0

使用包传递对象的另一种简单方法:

  • 在类对象中,使用键创建静态列表或其他数据结构
  • 创建对象时,请通过键将其放入列表/数据结构中(例如,创建对象时的较长时间戳记)
  • 创建方法静态getObject(long key)以从列表中获取对象
  • 捆绑包中的密钥,因此您可以稍后从代码的另一点获取对象
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.