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

时间:2011-02-02 18:11:18

标签: java android sharing

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

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

但是我想分享很多变量,有些变量可能相当大,所以我不想像上面那样创建它们的副本。

有没有办法直接获取和更改变量而不使用get和set方法?我记得在Google开发网站上阅读了一篇文章,称不建议在Android上使用此功能。

14 个答案:

答案 0 :(得分:456)

这是most common ways to achieve this

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

TL; DR :有两种共享数据的方式:在intent的附加内容中传递数据或将其保存在其他地方。如果数据是基元,字符串或用户定义的对象:将其作为意图附加内容的一部分发送(用户定义的对象必须实现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 String 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);

您可能需要或不必使用intent的附加内容传递对象ID。这一切都取决于你的具体问题。

将对象持久保存到磁盘

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

优点:您可以从其他地方启动活动,如果数据已经保留,则应该可以正常运行。

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

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

答案 1 :(得分:22)

你可以使用什么:

  1. 在活动之间传递数据(如Cristian所说)
  2. 使用一个包含大量静态变量的类(所以你可以在没有类实例的情况下调用它们而不使用getter / setter)
  3. 使用数据库
  4. 共享偏好设置
  5. 您选择的内容取决于您的需求。当你有“很多”时,你可能会使用多种方式

答案 2 :(得分:16)

google命令你做什么!在这里:http://developer.android.com/resources/faq/framework.html#3

  • 原始数据类型
  • 非持久对象
  • Singleton课程 - 我的最爱:D
  • 公共静态字段/方法
  • WeakReferences to Objects的HashMap
  • 持久对象(应用程序首选项,文件,contentProviders,SQLite DB)

答案 3 :(得分:14)

  

“但是我想分享很多   变量和一些可能是相反的   大,所以我不想创造   他们的副本就像上面一样。“

那不会复制(尤其是使用 String ,但是偶数对象都是通过引用的值传递的,而不是对象本身,并且getter就像这样可以使用 - 可以说更好使用比其他方式,因为它们是常见的,并且很好理解)。较旧的“性能神话”,例如不使用getter和setter,仍然有一些价值,但是have also been updated in the docs

但是如果你不想这样做,你也可以在 GlobalState 中公开或保护变量并直接访问它们。而且,您可以将静态单例设为Application object JavaDoc indicates

  

通常不需要子类   应用。在大多数情况下,   静态单例可以提供相同的   功能更加模块化。   如果你的单身人士需要全球化   上下文(例如注册   广播接收机),功能   检索它可以给出一个Context   内部使用   Context.getApplicationContext()的时候   首先构建单身人士。

使用 Intent 数据,这里的其他答案是另一种传递数据的方法,但它通常用于较小的数据和简单的类型。您可以传递更大/更复杂的数据,但它比仅使用静态单例更复杂。 Application对象仍然是我个人最喜欢的,用于在Android应用程序组件之间共享更大/更复杂的非持久性数据(因为它在Android应用程序中具有明确定义的生命周期)。

此外,正如其他人所说,如果数据非常复杂并且需要持久,那么您也可以使用SQLite或文件系统。

答案 4 :(得分:7)

您可以在所需的任何对象上扩展Application类和标记,然后可以在应用程序的任何位置使用它们

答案 5 :(得分:2)

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

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

答案 6 :(得分:2)

在各种活动之间分享数据有多种方法

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

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

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

      public static int sInitialValue=0;

使用classname.variableName;

在项目中的任何位置使用

3:使用数据库

但是它有点冗长的过程,你必须使用查询来插入数据并在需要时使用游标迭代数据。但是,如果不清除缓存,就不会丢失数据。

4:使用共享首选项

比数据库容易得多。但是有一些限制你无法保存ArrayList,List和custome对象。

5:在Aplication类中创建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();  

答案 7 :(得分:1)

我有一些想法,但我不知道它们是否符合您的要求。

您可以使用包含所有数据的服务,然后将您的活动绑定到服务以进行数据回溯。

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

这个可能完全不是您想要的,但您也可以尝试使用SharedPreferences或偏好。

无论哪种方式,让我知道你的决定。

答案 8 :(得分:1)

假设您使用Intent从活动1调用活动2 您可以使用intent.putExtra(),

传递数据

以此为参考。 Sending arrays with Intent.putExtra

希望这就是你想要的。

答案 9 :(得分:1)

如果您打算从当前活动中调用其他活动,则应使用Intents。您可以更少关注持久化数据,而不是根据需要进行共享。

但是,如果您确实需要保留这些值,那么您可以将它们保存在本地存储上的某种结构化文本文件或数据库中。属性文件,XML文件或JSON文件可以存储您的数据,并在活动创建期间轻松解析。不要忘记所有Android设备上都有SQLite,因此您可以将它们存储在数据库表中。您还可以使用Map来存储键值对并将映射序列化到本地存储,但这对于简单的数据结构来说可能太麻烦了。

答案 10 :(得分:1)

所有上述答案都很棒...我只是添加一个没人提到过通过活动持久保存数据,那就是使用内置的android SQLite数据库来保存相关数据......实际上你可以将您的databaseHelper置于应用程序状态,并在整个激活期间根据需要调用它。或者只需创建一个帮助程序类并在需要时进行数据库调用...只需添加另一个层供您考虑...但所有其他答案也足够了......真的只是偏好

答案 11 :(得分:1)

在活动之间共享数据 示例在登录后传递电子邮件

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

登录页面上的1个代码

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

主页上的2个代码

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

答案 12 :(得分:1)

有一种新的更好的活动之间共享数据的方法,它是LiveData。请特别注意Android开发人员页面上的以下引用:

  

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

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

答案 13 :(得分:0)

如果你想使用数据对象,这两个实现非常重要:

Serializable vs Parcelable

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

public class User implements Parcelable

here

中查看更多内容