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

发布于 2024-10-16 00:00:43 字数 364 浏览 3 评论 0原文

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

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

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

有没有一种方法可以不使用 get 和 set 方法直接获取和更改变量?我记得在 Google 开发网站上读到一篇文章说,为了在 Android 上提高性能,不建议这样做。

I have one activity which is the main activity used throughout the app and it has a number of variables. I have two other activities which I would like to be able to use the data from the first activity.
Now I know I can do something like this:

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

However I want to share a lot of variables and some might be rather large so I don't want to be creating copies of them like above.

Is there a way to directly get and change the variables without using get and set methods? I remember reading an article on the Google dev site saying this is not recommended for performance on Android.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

不羁少年 2024-10-23 00:00:43

这里汇总了实现此目的的最常见方法

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

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

如果您要传递原始数据或字符串,请使用此方法。您还可以传递实现Serialized 的对象。

尽管很诱人,但在使用 Serialized 之前您应该三思而后行:它很容易出错并且速度非常慢。所以一般来说:如果可能的话,远离Serialized。如果您想传递复杂的用户定义对象,请查看Parcelable接口。它更难实现,但与 Serialized 相比,它具有相当大的速度提升。

共享数据而不保留到磁盘

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

注意:有时,当用户离开您的 Activity(而不退出)时,Android 可能会决定终止您的应用程序。在这种情况下,我经历过 android 尝试使用应用程序被杀死之前提供的意图启动最后一个活动的情况。在这种情况下,存储在单例(您的或应用程序)中的数据将消失,并且可能会发生不好的事情。为了避免这种情况,您可以将对象持久保存到磁盘或在使用数据之前检查数据以确保其有效。

使用单例类

有一个类来保存数据:

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。这完全取决于您的具体问题。

将对象持久保存到磁盘

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

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

缺点:比较麻烦,需要更多的时间来实现。需要更多代码,因此引入错误的机会更大。它也会慢很多。

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

Here a compilation of most common ways to achieve this:

  • Send data inside intent
  • Static fields
  • HashMap of WeakReferences
  • Persist objects (sqlite, share preferences, file, etc.)

TL;DR: there are two ways of sharing data: passing data in the intent's extras or saving it somewhere else. If data is primitives, Strings or user-defined objects: send it as part of the intent extras (user-defined objects must implement Parcelable). If passing complex objects save an instance in a singleton somewhere else and access them from the launched activity.

Some examples of how and why to implement each approach:

Send data inside intents

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

On the second activity:

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

Use this method if you are passing primitive data or Strings. You can also pass objects that implements Serializable.

Although tempting, you should think twice before using Serializable: it's error prone and horribly slow. So in general: stay away from Serializable if possible. If you want to pass complex user-defined objects, take a look at the Parcelable interface. It's harder to implement, but it has considerable speed gains compared to Serializable.

Share data without persisting to disk

It is possible to share data between activities by saving it in memory given that, in most cases, both activities run in the same process.

Note: sometimes, when the user leaves your activity (without quitting it), Android may decide to kill your application. In such scenario, I have experienced cases in which android attempts to launch the last activity using the intent provided before the app was killed. In this cases, data stored in a singleton (either yours or Application) will be gone and bad things could happen. To avoid such cases, you either persist objects to disk or check data before using it to make sure its valid.

Use a singleton class

Have a class to hold the data:

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;}
}

From the launched activity:

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

Use application singleton

The application singleton is an instance of android.app.Application which is created when the app is launched. You can provide a custom one by extending 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;}
}

Before launching the activity:

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

Then, from the launched activity:

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

Static fields

The idea is basically the same as the singleton, but in this case you provide static access to the data:

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

From the launched activity:

String data = DataHolder.getData();

HashMap of WeakReferences

Same idea, but allowing the garbage collector to removed unreferenced objects (e.g. when the user quits the activity):

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();
  }
}

Before launching the activity:

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

From the launched activity:

DataHolder.getInstance().retrieve(someId);

You may or may not have to pass the object id using the intent’s extras. It all depends on your specific problem.

Persist objects to disk

The idea is to save the data in disk before launching the other activity.

Advantages: you can launch the activity from other places and, if the data is already persisted, it should work just fine.

Disadvantages: it’s cumbersome and takes more time to implement. Requires more code and thus more chance of introducing bugs. It will also be much slower.

Some of the ways to persist objects include:

寒冷纷飞旳雪 2024-10-23 00:00:43

您可以使用什么:

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

您选择的内容取决于您的需求。当你有“很多”时,你可能会使用不止一种方法

What you can use:

  1. passing data between activities (like Cristian said)
  2. using a class with a lot of static variables (so you can call them without an instance of the class and without using getter/setter)
  3. Using a database
  4. Shared Preferences

What you choose depends on your needs. Probably you will use more than one way when you have "a lot of"

将军与妓 2024-10-23 00:00:43

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

  • 原始数据类型
  • 非持久对象
  • Singleton 类 - 我最喜欢的 :D
  • 公共静态字段/方法
  • 对象弱引用的 HashMap
  • 持久对象(应用程序首选项、文件、contentProviders、SQLite DB)

Do what google commands you to do! here: http://developer.android.com/resources/faq/framework.html#3

  • Primitive Data Types
  • Non-Persistent Objects
  • Singleton class - my favorite :D
  • A public static field/method
  • A HashMap of WeakReferences to Objects
  • Persistent Objects (Application Preferences, Files, contentProviders, SQLite DB)
旧伤还要旧人安 2024-10-23 00:00:43

“但是我想分享很多
变量,有些可能相当
大所以我不想创造
像上面一样的副本。”

这不会产生副本(特别是对于String,但即使对象也是通过引用的值传递的,而不是对象本身,并且像这样的 getter 可以很好地使用-- 可以说比其他方法更好用,因为它们很常见并且很好理解)。 android.com/guide/practices/design/performance.html" rel="noreferrer">也在文档中进行了更新。

但是如果您不想这样做,您也可以只使用变量public 或 protected 在 GlobalState 中并直接访问它们,并且您可以将静态单例设置为 应用程序对象 JavaDoc 指示

通常不需要子类化
应用。在大多数情况下,
静态单例可以提供相同的
以更加模块化的方式实现功能。
如果您的单例需要全局
上下文(例如注册
广播接收器),该功能
检索它可以给定一个 Context
其内部使用
Context.getApplicationContext() 时
首先构建单例。

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

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

"However I want to share alot of
variables and some might be rather
large so I don't want to be creating
copies of them like above."

That doesn't make a copy (especially with String, but even objects are pass by value of the reference, not the object itself, and getter's like that are fine to use -- arguably better to use than other means because they are common and well understood). The older "performance myths," such as not using getters and setters, still have some value, but have also been updated in the docs.

But if you don't want to do that, you could also just make the variables public or protected in GlobalState and access them directly. And, you can make a static singleton as the Application object JavaDoc indicates:

There is normally no need to subclass
Application. In most situation,
static singletons can provide the same
functionality in a more modular way.
If your singleton needs a global
context (for example to register
broadcast receivers), the function to
retrieve it can be given a Context
which internally uses
Context.getApplicationContext() when
first constructing the singleton.

Using Intent data, as other answers here note is another way to pass data, but it's usually used for smaller data and simple types. You can pass larger/more complex data, but it's more involved than just using a static singleon. The Application object is still my personal favorite for sharing larger/more complex non persistent data between Android application components though (because it has a well defined lifecycle in an Android app).

Also, as others have noted, if the data gets very complex and needs to be persistent then you can use SQLite or the filesystem too.

凉栀 2024-10-23 00:00:43

有一种更好的新方法可以在 Activity 之间共享数据,它就是 LiveData。请特别注意 Android 开发者页面中的这段引用:

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

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

There is a new and better way to share data between activities, and it is LiveData. Notice in particular this quote from the Android developer's page:

The fact that LiveData objects are lifecycle-aware means that you can share them between multiple activities, fragments, and services. To keep the example simple, you can implement the LiveData class as a singleton

The implication of this is huge - any model data can be shared in a common singleton class inside a LiveData wrapper. It can be injected from the activities into their respective ViewModel for the sake of testability. And you no longer need to worry about weak references to prevent memory leaks.

始终不够 2024-10-23 00:00:43

您可以扩展 Application 类并标记在您想要的任何对象上,然后它们就可以在您的应用程序中的任何位置使用

You could extend the Application class and tag on any objects you want there, they are then available anywhere in your application

我不会写诗 2024-10-23 00:00:43

Activity 之间共享数据的方式有很多种

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

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

2:使用 static 关键字,将变量定义为 public 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();  

There are various way to share data between activities

1: Passing data between activities using Intent

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

2: Using static keyword , define variable as public static and use any where in project

      public static int sInitialValue=0;

use anywhere in project using classname.variableName;

3: Using Database

but its bit lengthy process, you have to use query for inserting data and iterate data using cursor when need. But there are no chance of losing data without cleaning cache.

4: Using shared Preferences

much easier than database. but there is some limitation you can not save ArrayList ,List and custome objects.

5: Create getter setter in Aplication class and access any where in project.

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

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

here set and get from activities

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

         String data=((YourApplicationClass)getApplicationContext()).getData();  
沫尐诺 2024-10-23 00:00:43

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

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

Using the hashmap of weak reference approach, described above, and in http://developer.android.com/guide/faq/framework.html seems problematic to me. How are entire entries reclaimed, not just the map value? What scope do you allocate it in? As the framework is in control of the Activity lifecycle, having one of the participating Activities own it risks runtime errors when the owner is destroyed in advance of its clients. If the Application owns it, some Activity must explicitly remove the entry to avoid the hashmap from holding on to entries with a valid key and a potentially garbaged collected weak reference. Furthermore, what should a client do when the value returned for a key is null?

It seems to me that a WeakHashMap owned by the Application or within a singleton is a better choice. An value in the map is accessed via a key object, and when no strong references to the key exist (i.e. all Activities are done with the key and what it maps to), GC can reclaim the map entry.

牵强ㄟ 2024-10-23 00:00:43

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

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

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

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

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

Well I have a few ideas, but I don't know if they are what your looking for.

You could use a service that holds all of the data and then just bind your activities to the service for data retrival.

Or package your data into a serializable or parcelable and attach them to a bundle and pass the bundle between activities.

This one may not be at all what your looking for, but you could also try using a SharedPreferences or a preference in general.

Either way let me know what you decide.

情域 2024-10-23 00:00:43

假设您使用意图从活动一调用活动二。
您可以使用intent.putExtra()传递数据,

仅供参考。
使用 Intent.putExtra 发送数组

希望这就是您想要的。

Assuming you are calling activity two from activity one using an Intent.
You can pass the data with the intent.putExtra(),

Take this for your reference.
Sending arrays with Intent.putExtra

Hope that's what you want.

月竹挽风 2024-10-23 00:00:43

如果您的目的是从当前 Activity 调用其他 Activity,则应使用 Intents。您的重点可能不是持久数据,而是根据需要共享数据。

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

If your intention is to call other Activities from the current Activity, you should use Intents. Your focus could be less on persisting data than on sharing it on an as-needed basis.

However, if you really need to persist these values then you could persist them in some kind of structured text file or database on local storage. A properties file, XML file, or JSON file could store your data and be easily parsed during activity creation. Don't forget also that you have SQLite on all Android devices, so you could store them in a database table. You could also use a Map to store key-value pairs and serialize the map to local storage, but this might be too cumbersome to be useful for simple data structures.

衣神在巴黎 2024-10-23 00:00:43

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

All of the aforementioned answers are great... I'm just adding one no one had mentioned yet about persisting data through activities and that is to use the built in android SQLite database to persist relevant data... In fact you can place your databaseHelper in the application state and call it as needed throughout the activates.. Or just make a helper class and make the DB calls when needed... Just adding another layer for you to consider... But all of the other answers would suffice as well.. Really just preference

伴随着你 2024-10-23 00:00:43

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

“电子邮件”是可用于引用正在请求的活动上的值的名称

1 登录页面上的代码

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

2 主页上的代码

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

Sharing data between activites
example passing an email after login

"email" is the name that can be used to reference the value on the activity that's being requested

1 Code on the login page

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

2 code on the home page

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");
梦幻的心爱 2024-10-23 00:00:43

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

Serialized 与 Parcelable

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

公共类 User 实现 Parcelable

这里

And if you wanna work with data object, this two implements very important:

Serializable vs Parcelable

  • Serializable is a marker interface, which implies the user cannot marshal the data according to their requirements. So when object implements Serializable Java will automatically serialize it.
  • Parcelable is android own serialization protocol. In Parcelable, developers write custom code for marshaling and unmarshaling. So it creates less garbage objects in comparison to Serialization
  • The performance of Parcelable is very high when comparing to Serializable because of its custom implementation
    It is highly recommended to use Parcelable implantation when serializing objects in android.

public class User implements Parcelable

check more in here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文