在活动之间共享数据的最佳方式是什么?

我有一个活动,这是整个应用程序中使用的主要活动,它有一些变量。我有另外两个活动,我希望能够使用来自第一个活动的数据。 现在我知道我可以这样做:

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();

然而,我想要分享很多变量,其中一些可能相当大,所以我不想像上面那样创建它们的副本。

是否有一种方法可以直接获取和更改变量而不使用get和set方法?我记得在谷歌开发网站上读过一篇文章,说不建议在Android上使用这种性能。

227823 次浏览

我有一些想法,但我不知道它们是否是你想要的。

您可以使用一个保存所有数据的服务,然后将活动绑定到该服务以进行数据检索。

或者将您的数据打包为可序列化或可打包的,并将它们附加到一个bundle中,并在活动之间传递该bundle。

这可能根本不是你要找的,但你也可以尝试使用SharedPreferences或一般的首选项。

不管怎样,让我知道你的决定。

下面是这是最常见的方法的编译:

  • 在意图内发送数据
  • 静态字段
  • WeakReferences的HashMap
  • 持久化对象(sqlite、共享首选项、文件等)

博士TL;:有两种方式共享数据:在意图的附加中传递数据或将其保存在其他地方。如果数据是原语、字符串或用户定义对象:将其作为意图额外内容的一部分发送(用户定义对象必须实现Parcelable)。如果传递复杂对象,则将实例保存在其他地方的单例中,并从启动的活动中访问它们。

一些如何以及为什么实现每种方法的例子:

在意图内发送数据

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);

关于第二个活动:

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");

使用这个方法如果你传递的是基本数据或字符串。你也可以传递实现Serializable的对象。

虽然很诱人,但在使用Serializable之前应该三思:它很容易出错,而且速度非常慢。所以一般来说:远离Serializable如果可能的话。如果你想传递复杂的用户定义对象,看看Parcelable接口。它更难实现,但与Serializable相比,它有相当大的速度增益。

共享数据而不持久化到磁盘

在大多数情况下,两个活动都运行在同一个进程中,因此可以通过将数据保存在内存中来在活动之间共享数据。

注意:有时,当用户离开你的活动(没有退出它),Android可能决定杀死你的应用程序。在这种情况下,我有经验的情况下,android试图启动最后一个活动使用的意图提供之前的应用程序被杀死。在这种情况下,存储在单例对象(你的或Application)中的数据将消失,可能会发生糟糕的事情。为了避免这种情况,要么将对象持久化到磁盘,要么在使用数据之前检查数据以确保其有效。

使用单例类

有一个类来保存数据:

public class DataHolder {
private String data;
public String getData() {return data;}
public void setData(String data) {this.data = data;}


private static final DataHolder holder = new DataHolder();
public static DataHolder getInstance() {return holder;}
}

从启动的活动:

String data = DataHolder.getInstance().getData();

使用单例应用程序

应用程序单例是应用程序启动时创建的android.app.Application实例。你可以通过扩展Application来提供一个自定义的:

import android.app.Application;
public class MyApplication extends Application {
private String data;
public String getData() {return data;}
public void setData(String data) {this.data = data;}
}

活动开始前:

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);

然后,从启动的活动中:

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();

静态字段

其思想基本上与单例相同,但在这种情况下,你提供了对数据的静态访问:

public class DataHolder {
private static String data;
public static String getData() {return data;}
public static void setData(String data) {DataHolder.data = data;}
}

从启动的活动:

String data = DataHolder.getData();

WeakReferences的HashMap

同样的想法,但是允许垃圾收集器删除未引用的对象(例如当用户退出活动时):

public class DataHolder {
Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();


void save(String id, Object object) {
data.put(id, new WeakReference<Object>(object));
}


Object retrieve(String id) {
WeakReference<Object> objectWeakReference = data.get(id);
return objectWeakReference.get();
}
}

活动开始前:

DataHolder.getInstance().save(someId, someObject);

从启动的活动:

DataHolder.getInstance().retrieve(someId);

你可能需要也可能不需要使用意图的附加来传递对象id。这完全取决于你的具体问题。

将对象持久化到磁盘

其思想是在启动其他活动之前将数据保存到磁盘中。

优点:你可以从其他地方启动活动,如果数据已经被持久化,它应该工作正常。

缺点:它很麻烦,需要更多的时间来实现。需要更多的代码,因此更有可能引入错误。它也会慢得多。

持久化对象的一些方法包括:

假设你正在使用Intent从activity 1调用activity 2 你可以通过intent.putExtra(),

来传递数据 拿这个作为你的参考。 使用Intent.putExtra发送数组 < / p >

希望这是你想要的。

如果你的意图是从当前Activity调用其他Activity,你应该使用意图。您可以将重点放在按需共享数据而不是持久化数据上。

但是,如果您确实需要持久化这些值,那么您可以将它们持久化在本地存储的某种结构化文本文件或数据库中。属性文件、XML文件或JSON文件可以存储您的数据,并且在活动创建期间可以很容易地进行解析。别忘了你在所有Android设备上都有SQLite,所以你可以把它们存储在一个数据库表中。还可以使用Map存储键-值对并将映射序列化到本地存储,但这对于简单的数据结构来说可能太麻烦了。

你可以用什么:

  1. 在活动之间传递数据(如Cristian所说)
  2. 使用带有大量静态变量的类(这样就可以在不使用类实例和getter/setter的情况下调用它们)
  3. 使用数据库
  4. 共同的喜好

你选择什么取决于你的需要。当你使用"a lot of"时,你可能会使用不止一种方式

"然而我想分享很多 变量,有些可能是 很大,所以我不想创造 他们的副本如上所示。" < / p >

这并不会产生复制(特别是在字符串中,但即使是对象也是通过引用的值传递的,而不是对象本身,像这样的getter是可以使用的——可以说比其他方法更好使用,因为它们是常见的和易于理解的)。以前的“性能神话”,比如不使用getter和setter,仍然有一些价值,但是在文档中也有更新吗. c。

但如果你不想这样做,你也可以在GlobalState中将变量设为公共或受保护,并直接访问它们。并且,你可以创建一个静态单例,如应用程序对象JavaDoc表示:

通常不需要子类化 应用程序。在大多数情况下, 静态单例也可以提供同样的功能 以更模块化的方式实现功能。 如果你的单例需要全局 上下文(例如注册 广播接收机),功能到 检索时可以给它一个Context 内部使用 Context.getApplicationContext()时

使用意图数据是传递数据的另一种方式,但它通常用于较小的数据和简单类型。您可以传递更大/更复杂的数据,但这比使用静态单leon更复杂。应用程序对象仍然是我个人最喜欢的在Android应用程序组件之间共享更大/更复杂的非持久数据的对象(因为它在Android应用程序中有一个定义良好的生命周期)。

另外,正如其他人所注意到的,如果数据变得非常复杂而且需要坚持不懈,那么你也可以使用SQLite或文件系统。

你可以扩展应用程序类并在你想要的任何对象上标记,然后它们在你的应用程序中的任何地方都可用

做谷歌命令你做的事!: http://developer.android.com/resources/faq/framework.html#3

  • 基本数据类型
  • 非持久化对象
  • 单例类-我最喜欢的:D
  • 一个公共静态字段/方法
  • 对象弱引用的HashMap
  • 持久对象(应用程序首选项,文件,内容提供程序,SQLite DB)

上面提到的所有答案都很棒……我只是添加了一个没有人提到的通过活动持久化数据,那就是使用android内置的SQLite数据库持久化相关数据…事实上,您可以将databaseHelper置于应用程序状态,并在整个激活过程中根据需要调用它。或者只是创建一个helper类,并在需要时调用DB…只是增加了另一层供你考虑……但所有其他答案也都足够了。只是偏好

使用弱引用方法的hashmap,如上所述,在http://developer.android.com/guide/faq/framework.html中对我来说似乎有问题。如何回收整个条目,而不仅仅是映射值?你在什么范围内分配它?由于框架控制着Activity的生命周期,如果在其客户端之前销毁了它的所有者,那么让其中一个参与的Activity拥有它将冒运行时错误的风险。如果应用程序拥有它,一些活动必须显式地删除该条目,以避免hashmap持有具有有效键和可能被垃圾收集的弱引用的条目。此外,当一个键的返回值为空时,客户端应该做什么?

在我看来,由应用程序拥有或在单例中拥有WeakHashMap是更好的选择。映射中的值是通过键对象访问的,当不存在对键的强引用时(即所有活动都使用键及其映射到的对象完成),GC可以回收映射条目。

活动之间共享数据 使用实例在登录

后发送邮件

“email”是可以用来引用正在请求的活动的值的名称

1登录界面代码

Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
openLoginActivity.putExtra("email", getEmail);

主页上的2个代码

Bundle extras = getIntent().getExtras();
accountEmail = extras.getString("email");

如果你想处理数据对象,这两个实现非常重要

Serializable和Parcelable

  • Serializable是一个标记接口,这意味着用户不能根据自己的需求编组数据。因此,当对象实现Serializable时,Java将自动序列化它。
  • Parcelable是android自己的序列化协议。在Parcelable中,开发人员编写用于封送和解封送的自定义代码。因此,与Serialization相比,它创建的垃圾对象更少
  • 与Serializable相比,Parcelable的性能非常高,因为它是自定义实现的 强烈建议在android中序列化对象时使用Parcelable植入

public class User implements Parcelable

在这里中查看更多信息

在活动之间共享数据有多种方式

1:使用Intent在活动之间传递数据

Intent intent=new Intent(this, desirableActivity.class);
intent.putExtra("KEY", "Value");
startActivity(intent)

2:使用静态关键字,将变量定义为公共静态,并在项目中使用任何位置

      public static int sInitialValue=0;

在项目的任何地方使用classname.variableName;

3:使用数据库

但其过程较长,插入数据时必须使用查询,需要时使用游标迭代数据。但是如果不清理缓存就不会丢失数据。

4:使用共享首选项

比数据库简单多了。但是有一些限制,你不能保存ArrayList,List和自定义对象。

5:在application类中创建getter setter,并访问项目中的任何地方。

      private String data;
public String getData() {
return data;
}


public void setData(String data) {
this.data = data;
}

这里设置和获取活动

         ((YourApplicationClass)getApplicationContext()).setData("abc");


String data=((YourApplicationClass)getApplicationContext()).getData();

有一种新的更好的方式来在活动之间共享数据,它就是LiveData。特别要注意Android开发者页面上的这句话:

LiveData对象能够感知生命周期这一事实意味着您可以在多个活动、片段和服务之间共享它们。为了保持示例简单,可以将LiveData类实现为单例

这样做的意义是巨大的——任何模型数据都可以在LiveData包装器中的公共单例类中共享。为了可测试性,它可以从活动中注入到它们各自的ViewModel中。并且您不再需要担心弱引用来防止内存泄漏。