为什么我需要打包我的对象,即使我只需要将它发送到同一任务的另一个线程?实际上,我需要打开一个甚至可以在同一个线程(主线程)上运行的活动。
换句话说,为什么 Google 不提供一个 startActivity 版本,它采用通用对象广告参数而不是捆绑包来让我启动一个活动,以防我知道它在同一个进程中或(大多数时候)甚至相同线程(主要的)?
为什么我需要打包我的对象,即使我只需要将它发送到同一任务的另一个线程?实际上,我需要打开一个甚至可以在同一个线程(主线程)上运行的活动。
换句话说,为什么 Google 不提供一个 startActivity 版本,它采用通用对象广告参数而不是捆绑包来让我启动一个活动,以防我知道它在同一个进程中或(大多数时候)甚至相同线程(主要的)?
您不需要使用 Parcelable 将对象从一个活动传递到另一个活动。您可以将对象的引用存储在静态成员变量中,如下所示:
public class Globals {
public static MyObject myObject;
}
现在,在包含该对象的代码中,您只需执行以下操作:
Globals.myObject = object;
在新活动中,您可以像这样获得它:
doSomethingWith(Globals.myObject);
现在,话虽如此,您需要注意以下几点:
如果您的应用程序几乎随时都在后台,Android 可以终止您的进程。当用户然后返回到您的应用程序时,Android 将为您的应用程序创建一个新进程,然后它将仅重新创建位于活动堆栈顶部的活动(即:正在显示的活动)。在这种情况下,新创建的活动将无法通过访问获取 iobject,Globals.myObject
因为该进程是新创建的,并且该成员变量为空。
要解决这个问题,您可以:
Globals.myObject == null
并做出相应的反应 - 告诉用户他需要返回,或者只是自己返回,或显示对话框或其他)onSaveInstanceState()
(Android 将在将您的应用程序发送到后台之前执行此操作)并在onCreate()
希望这既能回答您的问题,又能解释如何处理它。
编辑:添加更多关于为什么 Intents 包含序列化(Parcelable)对象而不是对象本身的信息
当您调用时startActivity()
,startService()
Android 可能最终会在另一个进程中启动活动或服务。在这种情况下,如果您在 Intent 中传递了一个对象,Android 将需要序列化该对象以将其传递给其他进程。由于 Android 使用“隐式 Intent 解析”来确定哪个组件可以处理 Intent,调用者可能知道也可能不知道哪个组件将启动。
Android 保存 Intent 的内容有多种原因:
A. Android 可以随时终止进程。如果这样做并且用户想要返回到应用程序,Android 会创建一个新进程,然后根据需要在该进程中重新创建活动。要创建活动,Android 还需要使 Intent 可用于活动。如果该进程已被终止,则必须保存和恢复 Intent 中的任何“对象”。因为 Intent 包含序列化的对象,所以根据需要重新创建这些对象不是问题。B. PendingIntents 被 Android 用作操作系统作为 Intent 发送者的代理的一种方式。Android 组件可以创建一个 PendingIntent 并将其提供给操作系统,以便它可以在以后触发该 Intent 的发送。在实际发送 PendingIntent 时,发送组件可能处于活动状态,也可能不处于活动状态。这意味着可以在 PendingIntent 中传递的任何对象都必须能够被序列化,这样即使调用组件不再存在,Android 也可以保留它。
Intent 并不打算作为组件之间的一般“参数传递”机制。当然你可以这样使用它,但你也可以使用其他(更简单的)机制。在给定的进程中,您可以使用标准 Java 机制传递对象。为此使用静态(类)变量没有任何问题。
Android 官方文档(在 FAQ 中)提供了很多关于如何传递复杂数据结构的信息。
如何在单个应用程序中的活动/服务之间传递数据? http://developer.android.com/guide/faq/framework.html#3
原始数据类型
要在应用程序中的活动/服务之间共享原始数据,请使用 Intent.putExtras()。为了传递需要持久化的原始数据,请使用 Preferences 存储机制。非持久对象
对于短期共享复杂的非持久性用户定义对象,建议采用以下方法:
单例类
您可以通过使用单例来利用您的应用程序组件在同一进程中运行这一事实。这是一个设计为只有一个实例的类。它有一个名为 getInstance() 的静态方法,它返回实例;第一次调用此方法时,它会创建全局实例。因为所有调用者都获得相同的实例,所以他们可以将其用作交互点。例如,活动 A 可以检索实例并调用 setValue(3);稍后的活动 B 可能会检索实例并调用 getValue() 以检索最后设置的值。
公共静态字段/方法
使数据可跨活动/服务访问的另一种方法是使用公共静态字段和/或方法。您可以从应用程序中的任何其他类访问这些静态字段。要共享一个对象,创建对象的活动会设置一个静态字段以指向该对象,任何其他想要使用该对象的活动只需访问该静态字段。
对象弱引用的 HashMap
您还可以使用弱引用的 HashMap 到具有长键的对象。当一个 Activity 想要将一个对象传递给另一个 Activity 时,它只需将该对象放入映射中并通过 Intent Extras 将密钥(这是一个基于计数器或时间戳的唯一 Long)发送给接收者 Activity。接收者活动使用此键检索对象。
持久对象
即使应用程序似乎继续运行,系统也可能会选择终止其进程并稍后重新启动它。如果您有数据需要从一个活动调用持续到下一个活动调用,则需要将该数据表示为在通知活动可能会消失时由活动保存的状态。
对于共享复杂的持久性用户定义对象,建议采用以下方法:
- Application Preferences - Files - contentProviders - SQLite DB
如果共享数据需要跨应用程序进程可能被终止的点保留,则将该数据放在持久存储中,如应用程序首选项、SQLite DB、文件或内容提供程序。有关如何使用这些组件的更多详细信息,请参阅数据存储。
为什么这是必要的:
因为Android独特的多任务能力和组件生命周期的想法。为了允许用户离开应用程序(比如使用活动 A、B 和 C,以及意图 i1、i2、i3),系统将应用程序分成组件。
这样,如果用户使用 Intent i2 启动 Activity B,但接到电话或查看他们的电子邮件,他们可以返回 Activity B,Android 系统可以重新交付 Intent i2。用户可以轻松无缝地从您的应用中中断的地方继续。
通过组件生命周期,您可以更轻松地进入和退出每个单独的组件。它还允许在不同类型的组件之间进行异步通信,例如BroadcastReceivers
,Services
和Activities
.
为此,操作系统执行所谓的“编组”。它将数据展平为原始数据类型(例如int
和char
),以便可以轻松地将其保存在内存中或写入临时存储以供以后检索。
此外,这对于任何类型的 IPC(进程间通信)都是必需的。
使用 anIntent
允许开发人员让 Android 操作系统执行封送处理——这通常会很乏味(并且可能很困难或有问题)。
Android 要求您在任务、活动、线程或服务之间传递的任何对象都必须扁平化为 aParcel
或 implement Serializable
。他们这样做是为了让您可以按值传递对象。当您将对象传递给方法或函数时,您是通过引用传递的。意图被传递给系统,系统决定将你的意图路由到哪里,是启动你的一个活动还是打开另一个应用程序。
如果接收对象不是您的应用程序的一部分,则接收器将无法访问您的应用程序的内存。因此,传递 Object 引用会导致接收应用程序崩溃。通过将对象展平为 Parcel 或 Serializable blob,接收者可以对发送的值进行操作,而无需访问原始引用。如果谷歌要实现一个可以传递基本对象的工具,它将要求他们为所有对象编写深拷贝函数,并且你将需要为所有自定义对象编写深拷贝函数,这会变得非常混乱、缓慢并且会占用相当多的系统内存,这可能会使 VM 溢出。通过为传递对象提供通用的基本设施,它们能够提高性能,减少内存需求,
如前所述,您可以创建对要在活动之间传递的对象的全局引用,只要它们都是您的应用程序的一部分。这不是最优雅的解决方案,并且要求您在使用完引用后将其清空,这样您就不会占用不必要的内存,但它运行得很好。
您创建的所有活动仅在 UI 线程上运行。没有其他办法。关于您关于需要包裹物品的问题,实际上这不是唯一的方法。您可以通过使对象也实现 Serializable 接口来传递对象。
在高层次上,Intents 是用于在不同组件之间进行通信的异步消息传递机制,例如:活动、服务和广播接收器。源组件和目标组件可能属于或不属于同一个应用程序(因此是进程),如果需要,Android 框架需要一种标准化的跨进程传递对象的方式。
为了在活动/服务/等之间传递您的自定义类对象。使用Bundle
within Intent
,您的类必须需要实现Parcelable
or Serializable
。
这就是你需要在推入之前包裹你的物体的原因Bundle