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

Cra*_*ool 233 java android sharing

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

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();
Run Code Online (Sandbox Code Playgroud)

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

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

Cri*_*ian 462

这里汇总了最常见的方法:

  • 在意图内发送数据
  • 静态字段
  • HashMap of WeakReferences
  • 持久对象(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);
Run Code Online (Sandbox Code Playgroud)

关于第二项活动:

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");
Run Code Online (Sandbox Code Playgroud)

如果要传递原始数据或字符串,请使用此方法.您还可以传递实现的对象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;}
}
Run Code Online (Sandbox Code Playgroud)

从推出的活动:

String data = DataHolder.getInstance().getData();
Run Code Online (Sandbox Code Playgroud)

使用应用程序单例

应用程序单例是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;}
}
Run Code Online (Sandbox Code Playgroud)

在启动活动之前:

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);
Run Code Online (Sandbox Code Playgroud)

然后,从已启动的活动:

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();
Run Code Online (Sandbox Code Playgroud)

静态字段

这个想法与单例基本相同,但在这种情况下,您提供对数据的静态访问:

public class DataHolder {
  private static String data;
  public static String getData() {return data;}
  public static String setData(String data) {DataHolder.data = data;}
}
Run Code Online (Sandbox Code Playgroud)

从推出的活动:

String data = DataHolder.getData();
Run Code Online (Sandbox Code Playgroud)

HashMap of WeakReferences

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

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

在启动活动之前:

DataHolder.getInstance().save(someId, someObject);
Run Code Online (Sandbox Code Playgroud)

从推出的活动:

DataHolder.getInstance().retrieve(someId);
Run Code Online (Sandbox Code Playgroud)

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

将对象保留到磁盘

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

优点:您可以从其他地方启动活动,如果数据已经保留,它应该可以正常工作.

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

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

  • 我认为这不是更大/更复杂数据的"正常"方式.使用静态单例或Application对象要容易得多,而且效果很好.现在说OP在示例中确实使用了String,为此,Intent是完美的首选. (11认同)
  • Serializable发现Android流程模型存在严重的性能问题.这就是为什么他们介绍了Parcelable.在上面的答案中读取*Parcelable*而不是*Serializable*. (10认同)
  • 这是通过`setResult`方法完成的.此外,在这种情况下,必须使用`startActivityForResult`方法调用辅助活动. (3认同)
  • 总结!关于单例被破坏的问题,对于具有大量活动和对象的应用程序有一个简单的解决方案:始终使用`Activity`的子类,并在其`onCreate()`中检查当你填充的单例的任何静态字段启动应用程序.如果该字段为空,请使用`FLAG_ACTIVITY_CLEAR_TASK`或`BroadcastReceiver`返回start活动以终止其他活动. (2认同)

War*_*ith 22

你可以用什么:

  1. 在活动之间传递数据(如Cristian所说)
  2. 使用具有大量静态变量的类(因此您可以在没有类的实例且不使用getter/setter的情况下调用它们)
  3. 使用数据库
  4. 共享首选项

您的选择取决于您的需求.当你有"很多"时,你可能会使用多种方式


小智 16

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

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


Cha*_*ins 14

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

这不会复制(特别是使用String,但是偶数对象都是通过引用的值传递,而不是对象本身,并且getter就像使用它一样好 - 可以说比其他方法更好用,因为它们很常见,完全了解).较旧的"性能神话",例如不使用getter和setter,仍然具有一定的价值,但也在文档中进行了更新.

但是如果您不想这样做,您也可以在GlobalState中将变量设置为public或protected,并直接访问它们.并且,您可以创建一个静态单例作为Application对象JavaDoc指示:

通常不需要子类Application.在大多数情况下,静态单例可以以更模块化的方式提供相同的功能.如果你的单例需要一个全局上下文(例如注册广播接收器),那么检索它的函数可以给一个Context,它在第一次构造单例时在内部使用Context.getApplicationContext().

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

此外,正如其他人所指出的那样,如果数据变得非常复杂并且需要持久化,那么您也可以使用SQLite或文件系统.

  • faq的那一部分现在似乎被删除了(我只看到"非持久对象"而没有提到Application类).无论如何你可以详细说明吗? (2认同)

Jim*_*mmy 7

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


Ami*_*val 7

有一种新的更好的方法可以在活动之间共享数据,那就是LiveData。请特别注意 Android 开发人员页面上的这句话:

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

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


aho*_*der 1

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

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

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

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

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