web-dev-qa-db-ja.com

Androidでのシングルトンとアプリケーションコンテキストの違い

これを思い出して シングルトンを使うことのいくつかの問題を列挙して そしてシングルトンパターンを使ったAndroidアプリケーションのいくつかの例を見たので、グローバルなアプリケーション状態を通して共有される単一インスタンスの代わりにシングルトンを使うのは良い考えかな.os.Applicationおよびcontext.getApplication()を介して取得します。

両方のメカニズムにどのような利点/欠点がありますか?

正直に言うと、私はこの記事で同じ答えを期待しています Webアプリケーションとのシングルトンパターン、お勧めできません! しかしAndroidに適用されます。私は正しいですか?それ以外のDalvikVMの違いは何ですか?

編集:いくつかの側面について意見を述べたいと思います。

  • 同期
  • 再利用性
  • テスト
356
mschonaker

私はDianne Hackbornの反応に非常に反対します。あなたがそれらを実際に必要とするとき容易に作り直されることができる軽量の、タスクスコープのオブジェクトを支持して我々は我々のプロジェクトからすべてのシングルトンを少しずつ取り除いています。

シングルトンはテストのための悪夢であり、遅延初期化されると、微妙な副作用を伴う"state indeterminism"を導入します(これはあるスコープから別のスコープへgetInstance()への呼び出しを移動すると突然表面化するかもしれません)。可視性は別の問題として言及されており、シングルトンは共有状態への--- "global"(= random)アクセスを暗示しているため、並行アプリケーションで正しく同期されていないと微妙なバグが発生する可能性があります。

私はそれをアンチパターンと考えています、それは本質的にグローバルな状態を維持することを意味する悪いオブジェクト指向のスタイルです。

あなたの質問に戻るには:

アプリコンテキストはそれ自体シングルトンと見なすことができますが、フレームワーク管理されており、明確に定義されたライフサイクル、スコープ、およびアクセスパスがあります。ですから、もしあなたがapp-globalの状態を管理する必要があるのなら、それはここでは他にはないはずです。それ以外の場合は、本当にシングルトンオブジェクトが必要な場合、または代わりに小さな短いインスタンスを作成するようにシングルトンクラスを書き直すことも可能である場合は、再考してください。手元のタスクを実行する生きているオブジェクト。

291
Matthias

私はとてもシングルトンをお勧めします。コンテキストが必要なシングルトンがある場合は、

MySingleton.getInstance(Context c) {
    //
    // ... needing to create ...
    sInstance = new MySingleton(c.getApplicationContext());
}

アプリケーション全体のグローバルな状態をすべて維持する必要がある場所を1つにするのではなく、アプリケーションを整理してモジュール化するのではなく、アプリケーションよりもシングルトンを使用するほうが賢明です。また、Application.onCreate()ですべての初期化を事前に行うことで、シングルトンが遅延して(要求に応じて)初期化されるという事実もあります。

シングルトンを使用しても本質的な問題はありません。理にかなっている場合は、正しく使用してください。 Androidフレームワークには実際に多くのものがあります。ロードされたリソースなどのプロセスごとのキャッシュを維持するためです。

また、単純なアプリケーションでは、マルチスレッドがシングルトンで問題になることはありません。設計上、アプリケーションへのすべての標準コールバックはプロセスのメインスレッドでディスパッチされるため、スレッドまたはスレッドを通して明示的に導入しない限りマルチスレッドは発生しません。暗黙のうちに、コンテンツプロバイダまたはサービスIBinderを他のプロセスに公開することによって。

あなたがしていることについてだけ考えてください。 :)

226
hackbod

から: 開発者>参照 - アプリケーション

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

20
Somatik

私は同じ問題を抱えていた:シングルトンやサブクラスAndroid.os.Applicationを作る?

最初に私はシングルトンを試してみましたが、ある時点で私のアプリはブラウザを呼び出します

Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));

そして問題は、受話器に十分なメモリがない場合、ほとんどのクラス(シングルトンでさえ)がいくらかのメモリを得るためにきれいにされるので、ブラウザから私のアプリに戻るとき、それは毎回クラッシュしました。

解決策:必要なデータをApplicationクラスのサブクラスの中に入れてください。

11
JoséMi

アプリケーションはシングルトンと同じではありません。理由は以下のとおりです。

  1. アプリケーションのメソッド(onCreateなど)がUIスレッドで呼び出されます。
  2. シングルトンのメソッドはどのスレッドでも呼び出すことができます。
  3. Applicationの "onCreate"メソッドでは、Handlerをインスタンス化できます。
  4. シングルトンがnone-uiスレッドで実行されると、Handlerをインスタンス化できません。
  5. アプリケーションには、アプリ内のアクティビティのライフサイクルを管理する機能があります。「registerActivityLifecycleCallbacks」メソッドがあります。
10
sunhang

同時に両方を考えてください。

  • シングルトンオブジェクトをクラス内の静的インスタンスとして持つ。
  • アプリケーション内のすべてのsingeltonオブジェクトのシングルトンインスタンスを返す共通クラス(Context)を持つこと。これは、Context内のメソッド名が意味を持つという利点があります。

さらに、シングルトンオブジェクトへのアクセスだけでなく、グローバルにアクセスする必要がある機能(context.logOffUser()、context.readSavedData()など)を含めるようにContextを拡張することをお勧めします。ファサードはその時理にかなっているでしょう。

5
adranale

彼らは実際には同じです。私が見ることができる一つの違いがあります。 Applicationクラスでは、Application.onCreate()で変数を初期化し、Application.onTerminate()でそれらを破棄することができます。シングルトンではVM初期化の初期化と破棄を頼る必要があります。

4
Fedor

ことわざのある馬の口から….

アプリを開発するときは、アプリ間でデータ、コンテキスト、またはサービスをグローバルに共有する必要があるかもしれません。たとえば、現在ログインしているユーザーなど、アプリにセッションデータがある場合は、この情報を公開することをお勧めします。 Androidでは、この問題を解決するためのパターンは、Android.app.Applicationインスタンスにすべてのグローバルデータを所有させてから、Applicationインスタンスをさまざまなデータやサービスへの静的アクセサを持つシングルトンとして扱うことです。

Androidアプリを作成するとき、Android.app.Applicationクラスのインスタンスは1つだけであることが保証されているので、それをシングルトンとして扱うのが安全です(そしてGoogle Androidチームによって推奨されます)。つまり、アプリケーションの実装に静的なgetInstance()メソッドを安全に追加できます。そのようです:

public class AndroidApplication extends Application {

    private static AndroidApplication sInstance;

    public static AndroidApplication getInstance(){
        return sInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sInstance = this;
    }
}
3
RMcGuigan

私の2セント:

私の活動が破壊されたときにいくつかのシングルトン/静的フィールドがリセットされたことに気づきました。私はいくつかのローエンド2.3デバイスでこれに気付きました。

私の場合は非常に単純でした。私はactivity.onCreate()から呼び出したプライベートフィールド "init_done"と静的メソッド "init"があります。 initのメソッドがアクティビティの再作成時に再実行されていたことに気付きました。

私の主張を証明することはできませんが、シングルトン/クラスが最初に作成/使用されたときに関連している可能性があります。アクティビティが破壊されたりリサイクルされたりすると、このアクティビティだけが参照しているすべてのクラスもリサイクルされるようです。

私はsingletonのインスタンスをApplicationのサブクラスに移動しました。アプリケーションインスタンスからアクセスします。そして、それ以来、二度と問題に気付かなかった。

これが誰かに役立つことを願っています。

3
Christ

私のアクティビティはfinish()を呼び出し(すぐには終了しませんが、最終的には終了します)、Google Street Viewerを呼び出します。 Eclipseでデバッグすると、Street Viewerが呼び出されるとアプリへの接続が切断されます。これは、(アプリケーション全体が閉じられていると理解しています。)メモリを解放するためです。 。それにもかかわらず、私はonSaveInstanceState()を介してバンドルに状態を保存し、スタック内の次のアクティビティのonCreate()メソッドでそれを復元することができます。静的シングルトンまたはサブクラス化アプリケーションを使用することによって、私はアプリケーションを閉じて状態を失うことに直面します(バンドルに保存しない限り)。だから私の経験から彼らは国家保存に関しては同じです。私は、Android 4.1.2および4.2.2では接続が失われたが、4.0.7または3.2.4では接続が失われていないことに気付きました。これは、メモリ回復メカニズムがある時点で変更されたことを示唆しています。

2
Piovezan