簡體   English   中英

在活動之間共享數據的最佳方式是什么?

[英]What's the best way to share data between activities?

我有一個活動,它是整個應用程序中使用的主要活動,它有許多變量。 我還有另外兩個活動,我希望能夠使用第一個活動中的數據。 現在我知道我可以做這樣的事情:

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

但是我想分享很多變量,有些變量可能相當大,所以我不想像上面那樣創建它們的副本。

有沒有辦法在不使用 get 和 set 方法的情況下直接獲取和更改變量? 我記得在 Google 開發網站上讀過一篇文章,說不建議在 Android 上提高性能。

這是實現這一目標最常見方法的匯編:

  • 在意圖內發送數據
  • 靜態字段
  • WeakReferences HashMap
  • 持久化對象(sqlite、共享首選項、文件等)

TL;DR :有兩種共享數據的方式:在意圖的附加內容中傳遞數據或將其保存在其他地方。 如果數據是基元、字符串或用戶定義的對象:將其作為意圖附加項的一部分發送(用戶定義的對象必須實現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");

如果您要傳遞原始數據或 Strings ,請使用此方法。 您還可以傳遞實現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。 這一切都取決於您的具體問題。

將對象持久化到磁盤

這個想法是在啟動其他活動之前將數據保存在磁盤中。

優點:您可以從其他地方啟動活動,如果數據已經持久化,它應該可以正常工作。

缺點:繁瑣,實施時間較長。 需要更多的代碼,因此有更多的機會引入錯誤。 它也會慢很多。

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

你可以使用什么:

  1. 在活動之間傳遞數據(如 Cristian 所說)
  2. 使用具有大量靜態變量的類(因此您可以在沒有類的實例和不使用 getter/setter 的情況下調用它們)
  3. 使用數據庫
  4. 共享偏好

您選擇什么取決於您的需求。 當您有“很多”時,您可能會使用不止一種方式

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

  • 原始數據類型
  • 非持久對象
  • Singleton 類 - 我最喜歡的 :D
  • 公共靜態字段/方法
  • 對對象的弱引用的 HashMap
  • 持久對象(應用程序首選項、文件、內容提供者、SQLite DB)

“但是我想共享很多變量,有些變量可能相當大,所以我不想像上面那樣創建它們的副本。”

這不會復制(尤其是使用String ,但即使對象也是通過引用的值傳遞,而不是對象本身,並且像這樣的 getter 可以很好地使用 - 可以說比其他方式更好用,因為它們很常見並且很好理解)。 較舊的“性能神話”,例如不使用 getter 和 setter,仍然有一些價值,但也在 docs 中進行了更新

但是,如果您不想這樣做,您也可以在GlobalState 中將變量設為public 或 protected 並直接訪問它們。 並且,您可以創建一個靜態單例,如Application 對象 JavaDoc 所示

通常不需要子類化應用程序。 在大多數情況下,靜態單例可以以更加模塊化的方式提供相同的功能。 如果您的單例需要全局上下文(例如注冊廣播接收器),則可以為檢索它的函數提供一個 Context,該 Context 在首次構造單例時在內部使用 Context.getApplicationContext()。

使用Intent數據,正如這里的其他答案所指出的那樣,是另一種傳遞數據的方式,但它通常用於較小的數據和簡單的類型。 您可以傳遞更大/更復雜的數據,但它比僅使用靜態單例更復雜。 Application對象仍然是我個人的最愛,用於在 Android 應用程序組件之間共享更大/更復雜的非持久性數據(因為它在 Android 應用程序中具有明確定義的生命周期)。

此外,正如其他人所指出的,如果數據變得非常復雜並且需要持久化,那么您也可以使用 SQLite 或文件系統。

你可以在你想要的任何對象上擴展Application類和標簽,然后它們在你的應用程序中的任何地方可用

有一種新的更好的方法可以在活動之間共享數據,那就是LiveData 請特別注意 Android 開發人員頁面上的這句話:

LiveData 對象具有生命周期感知能力這一事實意味着您可以在多個活動、片段和服務之間共享它們。 為了保持示例簡單,您可以將 LiveData 類實現為單例

這意味着巨大的 - 任何模型數據都可以在LiveData包裝器內的公共單例類中共享。 為了可測試性,它可以從活動注入到它們各自的ViewModel中。 而且您不再需要擔心弱引用來防止內存泄漏。

使用弱參考方法的哈希圖,如上所述,在http://developer.android.com/guide/faq/framework.html對我來說似乎有問題。 如何回收整個條目,而不僅僅是地圖值? 你把它分配在什么范圍內? 由於框架控制着 Activity 的生命周期,當所有者在其客戶端之前被銷毀時,讓其中一個參與的 Activity 擁有它會帶來運行時錯誤的風險。 如果應用程序擁有它,則某些 Activity 必須明確刪除該條目,以避免哈希圖持有具有有效鍵和潛在垃圾收集弱引用的條目。 此外,當一個鍵的返回值為空時,客戶端應該怎么做?

在我看來,應用程序或單例中擁有的 WeakHashMap 是更好的選擇。 映射中的值是通過鍵對象訪問的,當不存在對該鍵的強引用時(即所有活動都使用鍵及其映射到的內容完成),GC 可以回收映射條目。

有多種方式可以在活動之間共享數據

1:使用 Intent 在活動之間傳遞數據

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

上述所有答案都很棒......我只是添加一個沒有人提到的關於通過活動持久化數據的方法,那就是使用內置的android SQLite數據庫來持久化相關數據......事實上你可以放置你的databaseHelper 在應用程序狀態中,並在整個激活過程中根據需要調用它。以及..真的只是偏好

在登錄后傳遞電子郵件的活動之間共享數據示例

“電子郵件”是可用於引用所請求活動的值的名稱

1 登錄頁面上的代碼

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

2 主頁上的代碼

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

如果你想使用數據對象,這兩個實現非常重要:

可序列化與可打包

  • Serializable 是一個標記接口,這意味着用戶不能根據他們的要求編組數據。 所以當對象實現 Serializable Java 時會自動序列化它。
  • Parcelable 是 android 自己的序列化協議。 在 Parcelable 中,開發人員編寫用於編組和解組的自定義代碼。 因此,與序列化相比,它創建的垃圾對象更少
  • Parcelable 的性能與Serializable 相比非常高,因為它是自定義實現的。強烈建議在android 中序列化對象時使用Parcelable 植入。

public class User implements Parcelable

這里查看更多

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

您可以使用保存所有數據的服務,然后將您的活動綁定到該服務以進行數據檢索。

或者將您的數據打包成可序列化或可打包的,並將它們附加到一個包中,並在活動之間傳遞包。

這可能根本不是您想要的,但您也可以嘗試使用 SharedPreferences 或一般偏好。

無論哪種方式,讓我知道你的決定。

假設您使用 Intent 從活動一中調用活動二。
您可以使用 intent.putExtra() 傳遞數據,

拿這個給你參考。 使用 Intent.putExtra 發送數組

希望這就是你想要的。

如果您打算從當前活動中調用其他活動,則應使用Intents 您的重點可能不是持久化數據,而是按需共享數據。

但是,如果您確實需要保留這些值,那么您可以將它們保存在本地存儲上的某種結構化文本文件或數據庫中。 屬性文件、XML 文件或 JSON 文件可以存儲您的數據並在活動創建期間輕松解析。 也不要忘記您在所有 Android 設備上都有 SQLite,因此您可以將它們存儲在數據庫表中。 您還可以使用 Map 來存儲鍵值對並將映射序列化到本地存儲,但這對於簡單的數據結構來說可能太麻煩而無法使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM