即使我只需要将对象发送到同一任务的另一个线程,为什么我仍然需要打包它?实际上,我需要打开一个活动,即使在同一线程(主线程)上也能运行。
换句话说,为什么Google没有提供一个版本的startActivity,它接受一个通用对象作为参数而不是一个bundle,让我可以启动一个活动,以防我知道它在同一进程或(大多数时候)甚至是同一线程(主线程)中?
即使我只需要将对象发送到同一任务的另一个线程,为什么我仍然需要打包它?实际上,我需要打开一个活动,即使在同一线程(主线程)上也能运行。
换句话说,为什么Google没有提供一个版本的startActivity,它接受一个通用对象作为参数而不是一个bundle,让我可以启动一个活动,以防我知道它在同一进程或(大多数时候)甚至是同一线程(主线程)中?
您不需要使用Parcelable将对象从一个活动传递到另一个活动。您可以将该对象的引用存储在静态成员变量中,就像这样:
public class Globals {
public static MyObject myObject;
}
现在,在拥有该对象的代码中,您只需执行以下操作:
Globals.myObject = object;
在新的活动中,您可以像这样获取它:
doSomethingWith(Globals.myObject);
现在说到这里,你需要了解以下几点:
Android可以随时在后台结束你的应用程序进程。当用户返回到你的应用程序时,Android将为你的应用程序创建一个新的进程,然后它将只重新创建在活动栈顶部的活动(即显示的活动)。在这种情况下,新创建的活动将无法通过访问Globals.myObject来获取对象,因为进程已经重新创建并且该成员变量为空。
为了解决这个问题,你可以采取以下两种方法之一:
1.检查Globals.myObject == null以确定你的进程是否被杀死并重新启动,并相应地做出反应-告诉用户他需要返回,或者自己返回,或者显示对话框或其他内容;
2.在你的活动中,当Android调用onSaveInstanceState()时保存对象(Android在将你的应用程序发送到后台之前会这样做),并在onCreate()中恢复对象。
希望这些能回答你的问题并解释如何处理这个问题。
编辑:添加有关为什么意图包含序列化(Parcelable)对象而不是对象本身的更多信息
1.当你调用startActivity()或startService()时,Android可能会在另一个进程中启动活动或服务。在这种情况下,如果你在Intent中传递了一个对象,Android需要对该对象进行序列化才能将其传递到另一个进程中。由于Android使用的“隐式Intent解析”来确定哪个组件将处理Intent,调用方可能知道或不知道哪个组件将被启动。
2.Android保存意图的内容有各种原因:
A.Android可以随时终止进程。如果这样做并且用户想要返回到应用程序,则Android会创建一个新的进程,然后根据需要重新创建该进程中的活动。为了创建活动,Android还需要使意图可供活动使用。如果进程已被杀死,则Intents中的任何“对象”都必须被保存和恢复。因为意图包含序列化对象,所以可以根据需要重新创建这些对象,这不是问题。
B.PendingIntent是Android作为操作系统代理发送Intent的一种方式。Android组件可以创建PendingIntent,并将其提供给操作系统,以便在稍后的某个时间触发该Intent的发送。调用组件可能在PendingIntent实际发送时处于活动状态,也可能不处于活动状态。这意味着可以通过序列化传递在PendingIntent中传递的任何对象,以便Android即使没有调用组件也能保留它。
3.意图不是一个组件之间的通用“参数传递”机制。当然你可以这样使用它,但你也可以使用其他(更简单)的机制。在给定的进程内,你可以使用标准的Java机制传递对象。使用静态(类)变量进行此操作是没有问题的。singleTask
或singleInstance
。如果必要,您可以使用singleTop
来控制其余部分,但我仍然认为您可以使用这种机制来实现可靠性较高且开销较低的操作。 - David WasseronSaveInstanceState()
返回的Bundle
将传递给onCreate()
方法。这是正确的。但是...如果您的应用程序调用getIntent()
,原始的Intent
也将可用。 - David WasserAndroid官方文档(FAQ部分)提供了很多关于如何传递复杂数据结构的信息。
如何在单个应用程序中的活动和服务之间传递数据? http://developer.android.com/guide/faq/framework.html#3
Primitive Data Types
To share primitive data between Activities/Services in an application, use Intent.putExtras(). For passing primitive data that needs to persist use the Preferences storage mechanism. Non-Persistent Objects
For sharing complex non-persistent user-defined objects for short duration, the following approaches are recommended:
Singleton class
You can take advantage of the fact that your application components run in the same process through the use of a singleton. This is a class that is designed to have only one instance. It has a static method with a name such as getInstance() that returns the instance; the first time this method is called, it creates the global instance. Because all callers get the same instance, they can use this as a point of interaction. For example activity A may retrieve the instance and call setValue(3); later activity B may retrieve the instance and call getValue() to retrieve the last set value.
A public static field/method
An alternate way to make data accessible across Activities/Services is to use public static fields and/or methods. You can access these static fields from any other class in your application. To share an object, the activity which creates your object sets a static field to point to this object and any other activity that wants to use this object just accesses this static field.
A HashMap of WeakReferences to Objects
You can also use a HashMap of WeakReferences to Objects with Long keys. When an activity wants to pass an object to another activity, it simply puts the object in the map and sends the key (which is a unique Long based on a counter or time stamp) to the recipient activity via intent extras. The recipient activity retrieves the object using this key.
Persistent Objects
Even while an application appears to continue running, the system may choose to kill its process and restart it later. If you have data that you need to persist from one activity invocation to the next, you need to represent that data as state that gets saved by an activity when it is informed that it might go away.
For sharing complex persistent user-defined objects, the following approaches are recommended:
- Application Preferences - Files - contentProviders - SQLite DB
If the shared data needs to be retained across points where the application process can be killed, then place that data in persistent storage like Application Preferences, SQLite DB, Files or ContentProviders. Please refer to the Data Storage for further details on how to use these components.
为什么需要这样做:
由于Android具有多任务处理的独特能力和组件生命周期的概念。为了让用户离开应用程序(例如拥有活动A、B、C以及意图i1、i2、i3),系统将应用程序分成组件。
这样,如果用户使用意图i2启动Activity B,但接到电话或检查电子邮件,他们可以回到Activity B并且Android系统可以重新传递意图i2。用户可以轻松无缝地在您的应用程序中恢复之前的操作。
使用组件的生命周期可以使每个单独组件的进入和退出更加容易。它还允许不同类型的组件之间进行异步通信,例如BroadcastReceivers
、Services
和Activities
。
为了实现这一点,操作系统执行所谓的“marshaling”。它将数据压缩为基本数据类型(如int
和char
),以便可以轻松地保存在内存中或写入临时存储以供以后检索。
此外,任何类型的IPC(进程间通信)都需要这样做。
使用Intent
允许开发人员让Android操作系统执行marshaling,这通常是繁琐的(可能也很难或出现错误)。
Parcel
,或者实现Serializable
接口。这是为了让你能够通过值来传递对象。当你向方法或函数传递对象时,你通过引用来传递。意图被传递到系统中,系统会决定如何路由你的意图,无论是启动你的一个活动还是打开另一个应用。
如果接收对象不是你的应用程序的一部分,那么接收方将无法访问你的应用程序的内存。因此,通过传递一个对象引用会导致接收应用程序崩溃。通过将对象压缩成一个Parcel或Serializable blob,接收方可以在没有访问原始引用的情况下对发送的值进行操作。如果Google实现了一个可以传递基本对象的设施,他们将需要为所有对象编写深层拷贝函数,并且你将需要为所有自定义对象编写深层拷贝函数,这将变得非常混乱、缓慢并占用相当大的系统内存,可能会导致VM溢出。通过提供一个通用的基本传递对象的设施,它们能够加快性能、减少内存需求,并确保安全性,不允许其他应用程序引用你的应用程序内存。
然而,正如前面所提到的,你可以为想要在活动之间传递的对象创建全局引用,只要它们都是你的应用程序的一部分。这并不是最优雅的解决方案,需要在完成后将引用设置为空,以便不占用不必要的内存,但它足够好用。
所有的活动都只能在UI线程上创建运行。没有其他方法。关于您关于需要打包对象的问题,实际上这不是唯一的方法。您还可以通过使对象实现Serializable接口来传递对象。
在高层次上,Intent是一种异步消息传递机制,用于在不同组件之间进行通信,例如:活动、服务和广播接收器。源组件和目标组件可能属于同一个应用程序(因此进程)也可能不属于同一个应用程序,如果需要,在Android框架中需要一种标准化的方法来跨进程传递对象。
Bundle
和 Intent
传递自定义类对象,您的类必须实现 Parcelable
或 Serializable
接口。
这就是为什么您需要在将对象放入 Bundle
前对其进行打包的原因。