首页 文章

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

提问于
浏览
211

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

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

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

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

14 回答

  • 1

    这里是most common ways to achieve this的汇编:

    • 在意图内发送数据

    • 静态字段

    • 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);
    

    关于第二项活动:

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

    使用此方法 if you are passing primitive data or Strings . 您还可以传递实现 Serializable 的对象 .

    虽然诱人,但在使用 Serializable 之前你应该三思而行:它容易出错并且非常慢 . 所以一般来说: stay away from Serializable 如果可能的话 . 如果要传递复杂的用户定义对象, take a look at the Parcelable interface . 实施起来比较困难,但与 Serializable 相比,它具有相当大的速度提升 .

    无需持久保存到磁盘即可共享数据

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

    Note: 有时,当用户离开您的活动时(不退出),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 . 这一切都取决于您的具体问题 .

    将对象保留到磁盘

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

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

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

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

  • 0

    你可以用什么:

    • 在活动之间传递数据(如Cristian所说)

    • 使用具有大量静态变量的类(因此您可以在没有类的实例且不使用getter / setter的情况下调用它们)

    • 使用数据库

    • 共享首选项

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

  • 16

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

    • 原始数据类型

    • 非持久对象

    • Singleton类 - 我的最爱:D

    • 公共静态字段/方法

    • WeakReferences对象的HashMap

    • 持久对象(应用程序首选项,文件,contentProviders,SQLite DB)

  • 2

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

    这不会复制(特别是使用String,但是偶数对象都是通过引用的值传递,而不是对象本身,并且getter就像使用它一样好 - 可以说比其他方法更好用,因为它们很常见,完全了解) . 年纪越大"performance myths,"如不使用getter和setter,仍然有一些 Value ,但have also been updated in the docs .

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

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

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

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

  • 7

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

  • 1

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

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

  • 22

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

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

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

    这个可能根本不是你想要的,但你也可以尝试使用SharedPreferences或一般的偏好 .

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

  • 1

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

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

    希望这就是你想要的 .

  • 1

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

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

  • 14

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

  • 445

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

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

    1登录页面上的代码

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

    主页上有2个代码

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

    There are various way to share data between activities

    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();
    
  • 1

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

    Serializable vs Parcelable

    • Serializable是一个标记接口,这意味着用户无法根据需要封送数据 . 因此当对象实现Serializable时,Java会自动序列化它 .

    • Parcelable是android自己的序列化协议 . 在Parcelable中,开发人员编写用于编组和解组的自定义代码 . 因此,与序列化相比,它创建的垃圾对象更少

    • 与Serializable相比,Parcelable的性能非常高,因为它的自定义实现强烈建议在序列化android中的对象时使用Parcelable植入 .

    public class User implements Parcelable

    here查看更多内容

  • 1

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

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

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

相关问题