アクティビティ間でデータを共有する最良の方法は何ですか?


239

アプリ全体で使用されるメインアクティビティである1つのアクティビティがあり、いくつかの変数があります。最初のアクティビティからのデータを使用できるようにしたい他の2つのアクティビティがあります。これで、次のようなことができることがわかりました。

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

しかし、私は多くの変数を共有したいので、いくつかはかなり大きいので、上記のようにそれらのコピーを作成したくありません。

getおよびsetメソッドを使用せずに変数を直接取得および変更する方法はありますか?これはAndroidでのパフォーマンスには推奨されないというGoogle開発サイトの記事を読んだことを覚えています。


2
Android 2.3(Gingerbread)以降、get / setの最適化はDalvikによって自動的に実行されます。これは、古いバージョンのAndroidをターゲットにしている場合にのみ関係します。
StellarVortex 14年

この例では文字列データはコピーされないことに注意してください。むしろ、同じ文字列オブジェクトへの参照を作成します。
Code-Apprentice

信じられないですが、なぜ別のアクティビティからアクティビティを開始して、最初のアクティビティから2番目のアクティビティに複雑なオブジェクトを渡す可能性がないのですか?シリアル化せずに、オブジェクトとそのすべての労力を保存します。これはセキュリティホールですか?それとも、両方のアクティビティが同じアプリにある場合、オブジェクト参照を単に渡すことに対して他の理由がありますか?(別のアプリにあるかどうかは異なると
思い


LiveDataは最新の最良のソリューションです。以下の私の答えを確認してください。
Amir Uval

回答:


476

これを達成するための最も一般的な方法のコンパイル:

  • インテント内でデータを送信する
  • 静的フィールド
  • のハッシュマップ WeakReferences
  • 永続オブジェクト(sqlite、共有設定、ファイルなど)

TL; DR:データを共有する方法は2つあります。インテントのエクストラでデータを渡すか、別の場所に保存します。データがプリミティブ、文字列、またはユーザー定義オブジェクトの場合:インテントエクストラの一部として送信します(ユーザー定義オブジェクトはを実装する必要がありますParcelable)。複雑なオブジェクトを渡す場合、インスタンスを別の場所のシングルトンに保存し、起動されたアクティビティからそれらにアクセスします。

各アプローチを実装する方法と理由の例:

インテント内にデータを送信する

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

2番目のアクティビティ:

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 void setData(String data) {DataHolder.data = data;}
}

開始されたアクティビティから:

String data = DataHolder.getData();

のハッシュマップ 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();
  }
}

アクティビティを起動する前に:

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

開始されたアクティビティから:

DataHolder.getInstance().retrieve(someId);

インテントのエクストラを使用してオブジェクトIDを渡す必要がある場合とない場合があります。それはすべてあなたの特定の問題に依存します。

オブジェクトをディスクに永続化する

アイデアは、他のアクティビティを起動する前にデータをディスクに保存することです。

利点:他の場所からアクティビティを起動できます。データがすでに永続化されている場合は、問題なく機能するはずです。

短所:面倒で実装に時間がかかります。より多くのコードを必要とするため、バグが発生する可能性が高くなります。また、それははるかに遅くなります。

オブジェクトを永続化する方法には、次のようなものがあります。


11
私は、それがより大きな/より複雑なデータの「通常の」方法ではないと主張します。静的シングルトンまたはApplicationオブジェクトを使用する方がはるかに簡単で、優れた動作をします。OPがこの例で文字列を使用したと述べたので、そのためには、意図が完全であり、優先されます。
チャーリーコリンズ

10
Serializableは、Androidプロセスモデルで重大なパフォーマンスの問題があることがわかりました。それが、彼らがParcelableを導入した理由です。上記の回答でSerializableではなくParcelableをお読みください。
Subin Sebastian 2012

3
それはsetResultメソッドを介して行われます。また、その場合、startActivityForResultメソッドを使用して2次アクティビティを呼び出す必要があります。
クリスティアン

2
素晴らしい要約!利用のサブクラス:破壊されるシングルトンの問題に関しては、活動やオブジェクトのたくさんのアプリケーションのための簡単な解決策はありますActivity全体、そしてその中にonCreate()アプリを起動するときに、あなたが埋めることシングルトンのチェック任意の静的フィールド。そのフィールドが空の場合は、FLAG_ACTIVITY_CLEAR_TASKまたはa BroadcastReceiverを使用して開始アクティビティに戻り、他のアクティビティを強制終了します。
Janosch 2014年

1
アプリケーションクラスにデータを保存することはお勧めしません。詳しくはこちら:developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

使用できるもの:

  1. アクティビティ間でのデータの受け渡し(クリスチャンによると)
  2. 多くの静的変数を持つクラスを使用する(したがって、クラスのインスタンスなしで、getter / setterを使用せずにそれらを呼び出すことができます)
  3. データベースの使用
  4. 共有設定

何を選択するかは、ニーズによって異なります。たぶん「たくさん」を持っているとき、あなたは複数の方法を使うでしょう


1
静力学はプロセスの終了時にクリアされることに注意してください
EpicPandaForce

もちろん、@ EpicPandaForceとデバイスがオフにされたとき。
WarrenFaith 2017年

1
ただし、デバイスがオフになっている場合、アプリはMAINアクションから再起動します。プロセスが終了した後、最後に開いていたアクティビティから再開します。これは、アプリのどこかにある詳細ページである可能性があります。
EpicPandaForce 2017年

@EpicPandaForceは、あなたがCpt Obviousであっても、私の皮肉を逃したようです。
WarrenFaith 2017年

16

グーグルがあなたにするように命令することをしてください!ここ:http : //developer.android.com/resources/faq/framework.html#3

  • プリミティブデータ型
  • 非永続オブジェクト
  • シングルトンクラス-私のお気に入り:D
  • パブリック静的フィールド/メソッド
  • オブジェクトへのWeakReferencesのHashMap
  • 永続オブジェクト(アプリケーション設定、ファイル、contentProviders、SQLite DB)

1
永続オブジェクトのGoogleリンク:developer.android.com/guide/topics/data/data-storage.html
NicoMinsk

アンチパターンのみ、シングルトンクラスは最初のデザインパターンです。静的フィールド/メソッドを持つクラスはシングルトンのように振る舞い、busnissオブジェクトを永続化するためのデータベースを作成することは、良いことではありません。それを達成する方法ですが、なぜGoogleがこのような非常に愚かなこと、パフォーマンスの問題などを複雑にしたのでしょうか?または私はAndroidの方法を理解していませんか?? !!!!
La VloZ Merrill、2016

リンクが壊れています:(
Shayan_Aryan

14

「しかし、私は多くの変数を共有したいので、いくつかはかなり大きいので、上記のようにそれらのコピーを作成したくありません。」

これはコピーを作成しません(特にStringを使用しますが、オブジェクトでもオブジェクト自体ではなく参照の値によって渡されます)。そのようなゲッターは使用しても問題ありません。十分に理解)。getterやsetterを使用しないなどの古い「パフォーマンス神話」は、まだいくつかの価値がありますが、ドキュメントでも更新されています。

しかし、そうしたくない場合は、変数をpublicStateで publicまたはprotectedにして、それらに直接アクセスすることもできます。また、ApplicationオブジェクトのJavaDocが示すように、静的なシングルトンを作成できます

通常、Applicationをサブクラス化する必要はありません。ほとんどの状況で、静的シングルトンは、よりモジュール的な方法で同じ機能を提供できます。シングルトンにグローバルコンテキストが必要な場合(たとえば、ブロードキャストレシーバーを登録するため)、それを取得する関数に、最初にシングルトンを構築するときにContext.getApplicationContext()を内部的に使用するContextを指定できます。

インテントデータを使用することは、他の回答としてここに記載されているように、データを渡す別の方法ですが、通常はより小さなデータや単純な型に使用されます。より大きな/より複雑なデータを渡すことができますが、静的なシングルオンを使用するよりも複雑です。アプリケーションオブジェクトは、まだ(それがAndroidアプリでよく定義されたライフサイクルを持っているので)しかしAndroidのアプリケーションコンポーネント間のより大きな/より複雑な非永続データを共有するための私の個人的な好みです。

また、他の人が述べたように、データが非常に複雑になり、永続化する必要がある場合は、SQLiteまたはファイルシステムも使用できます。


1
実際、私は最近、ドキュメントでこれに遭遇しました:developer.android.com/guide/appendix/faq/framework.html#3。「非永続的な複雑なオブジェクト」の場合は、Applicationクラスを使用して静的シングルトンを作成および破棄することをお勧めします。そうすることで、アプリケーションが提供する明確に定義されたライフサイクルと、静的なシングルトンの使いやすさが得られます。
チャーリーコリンズ

2
FAQのそのセクションは現在削除されているようです(「非永続オブジェクト」のみが表示され、Applicationクラスについての言及はありません)。とにかく、詳しく説明できますか?
トニーチャン

1
現在、developer.android.com / guide / faq / framework.html#3「単一のアプリケーション内のアクティビティ/サービス間でデータを渡すにはどうすればよいですか?」にあり、Applicationクラスについては言及されていません。
Jerry101 2014年

「Android in Action」で設定した先例に従って、Applicationオブジェクトを使いたい。しかし、コードの課題でこれを見ると、多くの将来の雇用主はそれを気に入らない。彼らは間違っている可能性がありますが、彼らは自分の体重を回ります。ところで、その「framework.html#3」リンクは機能しなくなりました。
マットJ.17年


4

アクティビティ間でデータを共有するための新しいより良い方法があり、それはLiveDataです。特に、Android開発者のページからの次の引用に注目してください。

LiveDataオブジェクトはライフサイクル対応であるため、複数のアクティビティ、フラグメント、およびサービス間でオブジェクトを共有できます。例を単純に保つために、LiveDataクラスをシングルトンとして実装できます

これの影響は非常に大きく、どのモデルデータもLiveDataラッパー内の共通のシングルトンクラスで共有できます。ViewModelテストを容易にするために、アクティビティからそれぞれのアクティビティに注入できます。また、メモリリークを防止するために、弱参照を心配する必要がなくなります。


2

上記およびhttp://developer.android.com/guide/faq/framework.htmlで説明されている弱参照アプローチのハッシュマップを使用することは、私には問題があるようです。マップ値だけでなく、エントリ全体がどのように再利用されますか?どの範囲に割り当てますか?フレームワークはアクティビティのライフサイクルを制御しているため、参加しているアクティビティの1つを所有すると、所有者がクライアントより先に破棄されると、ランタイムエラーが発生する危険があります。アプリケーションがそれを所有している場合、一部のアクティビティは、エントリを明示的に削除して、ハッシュマップが有効なキーと収集された弱参照の可能性のあるエントリを持つエントリを保持しないようにする必要があります。さらに、キーに対して返された値がnullの場合、クライアントは何をすべきですか?

アプリケーションが所有している、またはシングルトン内にあるWeakHashMapの方が適切な選択のようです。マップの値はキーオブジェクトを介してアクセスされ、キーへの強い参照が存在しない場合(つまり、すべてのアクティビティがキーとキーのマッピング先で行われる場合)、GCはマップエントリを再利用できます。


2

アクティビティ間でデータを共有するにはさまざまな方法があります

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:アプリケーションクラスでゲッターセッターを作成し、プロジェクトの任意の場所にアクセスします。

      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

まあ私はいくつかのアイデアを持っていますが、それらがあなたの探しているものかどうかはわかりません。

すべてのデータを保持するサービスを使用して、データを取得するためにアクティビティをサービスにバインドすることができます。

または、データをシリアル化可能またはパーセル可能にパッケージ化してバンドルに添付し、アクティビティ間でバンドルを渡します。

これはあなたが探しているものではないかもしれませんが、SharedPreferencesまたは一般的な設定を使用することもできます。

どちらにしても、あなたが決定したことを私に知らせてください。



1

現在のアクティビティから他のアクティビティを呼び出す場合は、インテントを使用する必要があります。必要に応じてデータを共有することよりも、データを永続化することに重点を置くことができます。

ただし、これらの値を永続化する必要がある場合は、ローカルストレージ上の何らかの構造化テキストファイルまたはデータベースに永続化できます。プロパティファイル、XMLファイル、またはJSONファイルは、データを保存し、アクティビティの作成中に簡単に解析できます。すべてのAndroidデバイスにSQLiteがあり、データベーステーブルに保存できることを忘れないでください。マップを使用してキーと値のペアを保存し、ローカルストレージにマップをシリアル化することもできますが、これは扱いにくいので、単純なデータ構造には役立ちません。


1

前述の答えはすべて素晴らしいです...アクティビティを通じてデータを永続化することについて誰もまだ言及していなかった1つを追加します。それは、組み込みのAndroid SQLiteデータベースを使用して関連データを永続化することです...実際にアプリケーションの状態でdatabaseHelperを呼び出し、アクティベート全体で必要に応じてそれを呼び出します。または、ヘルパークラスを作成し、必要に応じてDB呼び出しを行います...検討する別のレイヤーを追加するだけです...しかし、他のすべての答えで十分です。同様に..本当に好みだけ


1

ログイン後にメールを渡してアクティビティ間でデータを共有する例

「email」は、要求されているアクティビティーの値を参照するために使用できる名前です

ログインページの1コード

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

ホームページに2つのコード

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

1

また、データオブジェクトを操作する場合、この2つの実装は非常に重要です。

シリアライズ可能vsパーセル可能

  • Serializableはマーカーインターフェイスであり、ユーザーが要件に従ってデータをマーシャリングできないことを意味します。したがって、オブジェクトがSerializable Javaを実装すると、自動的にシリアル化されます。
  • Parcelableは、Android独自のシリアル化プロトコルです。Parcelableでは、開発者がマーシャリングとマーシャリング解除のためのカスタムコードを記述します。したがって、シリアライゼーションと比較して作成されるガベージオブジェクトは少なくなります。
  • カスタム実装のため、ParcelableのパフォーマンスはSerializableと比較すると非常に高いです。Androidでオブジェクトをシリアル化する場合は、Parcelableの埋め込みを使用することを強くお勧めします。

public class User implements Parcelable

詳細はこちら

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.