どこでもアプリケーションコンテキストを使用していますか?


476

Androidアプリでは、次のアプローチに問題がありますか?

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

そして、コンテキストが必要なすべての場所(SQLiteOpenHelperなど)に渡します(もちろんリークしません)?


23
これを実装している他の人のために詳しく説明するため<application>に、AndroidManifest.xmlファイルのノードを変更して、次の属性定義を含めることができますandroid:name="MyApp"。MyAppは、マニフェストが参照するのと同じパッケージの下にある必要があります。
Matt Huggins、

6
SQLiteOpenHelperにコンテキストを提供する問題を回避するための素晴らしい方法!! シングルトン「SQLiteManager」を実装しましたが、「シングルトンへのコンテキストを取得するにはどうすればよいですか?」
誰かどこか

8
スーパーインターフェイスの1つでアプリケーションを返すことがわかっているので、MyApp内で追加のメソッドを提供すると、それらを使用できなくなります。getContext()の戻り値の型はMyAppである必要があります。これにより、後で追加されたメソッドや、ContextWrapperおよびContextのすべてのメソッドを使用できます。

5
goo.gl/uKcFnもご覧ください-同様の投稿に関連する別の返信です。c'torではなく、onCreateで静的変数を設定する方が適切です。
AlikElzin-kilaka、2011年

1
@ChuongPhamフレームワークがアプリを強制終了した場合、nullコンテキストにアクセスするものは何もありません...
Kevin Krumwiede

回答:


413

このアプローチにはいくつかの潜在的な問題がありますが、多くの状況(例など)ではうまく機能します。

特に、を必要とするものを扱う場合は注意GUIが必要Contextです。たとえば、アプリケーションのコンテキストをに渡すLayoutInflaterと、例外が発生します。一般的に言えば、あなたのアプローチが優れている:それは使用することをお勧めしますActivity's Contextその内Activity、およびApplication Contextの範囲を超えてコンテキストを渡すときにActivityするためにメモリリークを避けます

また、パターンの代わりgetApplicationContext()に、Contextオブジェクト(アクティビティなど)を呼び出すショートカットを使用して、アプリケーションコンテキストを取得することもできます。


22
感動的な答えをありがとう。私はこのアプローチを永続化レイヤーにのみ使用すると思います(コンテンツプロバイダーには使いたくないので)。アプリケーション自体から取得するのではなく、コンテキストが提供されることを期待する方法でSQLiteOpenHelperを設計する背後にある動機は何だったのかと思いました。PSそしてあなたの本は素晴らしいです!
yanchenko 2009年

7
アプリケーションコンテキストを使用するLayoutInflatorだけでうまくいきました。過去3年間で変更されている必要があります。
Jacob Phillips、

5
@JacobPhillipsアクティビティコンテキストなしでLayoutInflatorを使用すると、そのアクティビティのスタイリングを見逃します。したがって、ある意味では機能しますが、別の意味では機能しません。
マーク

1
@MarkCarterアプリケーションコンテキストを使用すると、アクティビティのスタイルが失われるということですか?
Jacob Phillips

1
@JacobPhillipsはい、すべてのアクティビティは異なる方法でスタイル設定される可能性があるため、アプリケーションコンテキストはスタイルを設定できません。
マーク

28

私の経験では、このアプローチは必要ありません。何かのコンテキストが必要な場合は、通常、View.getContext()Context呼び出して取得できます。そこで取得したコンテキストを使用して、Context.getApplicationContext()を呼び出してApplicationコンテキストを取得できます。これからApplicationコンテキストを取得しようとしているActivity場合は、いつでもActivity.getApplication()を呼び出すことができます。これは、Contextへの呼び出しに必要なときに渡すことができSQLiteOpenHelper()ます。

全体として、この状況に対するアプローチに問題はないように見えますが、対処するContext場合は、公式のGoogle Android Developersブログに記載されているように、どこにもメモリリークがないことを確認してください。


13

一部の人々は尋ねました:シングルトンはどのようにnullポインターを返すことができますか? 私はその質問に答えています。(コードを投稿する必要があるため、コメントには回答できません。)

2つのイベントの間にnullを返すことがあります。(1)クラスがロードされ、(2)このクラスのオブジェクトが作成されます。次に例を示します。

class X {
    static X xinstance;
    static Y yinstance = Y.yinstance;
    X() {xinstance=this;}
}
class Y {
    static X xinstance = X.xinstance;
    static Y yinstance;
    Y() {yinstance=this;}
}

public class A {
    public static void main(String[] p) {
    X x = new X();
    Y y = new Y();
    System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
    System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
    }
}

コードを実行してみましょう:

$ javac A.java 
$ java A
x:X@a63599 y:Y@9036e
x:null y:null

2行目は、Y.xinstanceX.yinstancenullであることを示しています。変数のため、彼らはnullでX.xinstance ANS Y.yinstanceは、彼らがnullだったとき読み取りました。

これは修正できますか?はい、

class X {
    static Y y = Y.getInstance();
    static X theinstance;
    static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
    static X x = X.getInstance();
    static Y theinstance;
    static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}

public class A {
    public static void main(String[] p) {
    System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
    System.out.println("x:"+Y.x+" y:"+X.y);
    }
}

そしてこのコードは異常を示していません:

$ javac A.java 
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e

ただし、これはAndroid Applicationオブジェクトのオプションではありません。プログラマは、オブジェクトが作成される時間を制御しません。

ここでも、最初の例と2番目の例の違いは、静的ポインタがnullの場合、2番目の例がインスタンスを作成することです。ただし、システムが作成する前に、プログラマー Androidアプリケーションオブジェクトを作成できません。

更新

初期化された静的フィールドが偶然に発生するもう1つの不可解な例null

Main.java

enum MyEnum {
    FIRST,SECOND;
    private static String prefix="<", suffix=">";
    String myName;
    MyEnum() {
        myName = makeMyName();
    }
    String makeMyName() {
        return prefix + name() + suffix;
    }
    String getMyName() {
        return myName;
    }
}
public class Main {
    public static void main(String args[]) {
        System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
        System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
        System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
    }
}

そしてあなたは得る:

$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull

静的変数宣言を1行上に移動できないことに注意してください。コードはコンパイルされません。


3
有用な例。そのような穴があることを知るのは良いことです。私がこれから取り除いていることは、クラスの静的初期化中にそのような静的変数を参照することを避けるべきであるということです。
ToolmakerSteve、2015年

10

アプリケーションクラス:

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {

    private static Context mContext;

    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static Context getAppContext() {
        return mContext;
    }

}

AndroidManifestでアプリケーションを宣言します。

<application android:name=".MyApplication"
    ...
/>

使用法:

MyApplication.getAppContext()

1
メモリリークが発生しやすい。あなたはこれをするべきではありません。
Dragas

9

アプリケーションコンテキストを取得するためのラッパーを作成しようとしていますが、 " null"ポインターを返す可能性があります。

私の理解によれば、私は2 Context.getApplicationContext() またはのいずれかを呼び出すためのより良いアプローチを推測します Activity.getApplication()


13
いつnullを返す必要がありますか?
スタック

25
私が知っている静的なContext.getApplicationContext()メソッドはありません。何か不足していますか?
ダルカンタラ

アプリケーションにも同じアプローチを実装していますが、SQLiteOpenHelperで呼び出すと、nullポインターが返されます。この種の状況に対する答え。
ashutosh

2
これは、アプリの前に読み込まれるコンテンツプロバイダーでSQLiteOpenHelperを呼び出す場合に当てはまります。
Gunnar Bernstein

5

それは良いアプローチです。私も使っています。onCreateコンストラクターを使用するのではなく、シングルトンを設定するためにオーバーライドすることをお勧めします。

そして、あなたが言及したのでSQLiteOpenHelperonCreate ()あなたもデータベースを開くことができます。

個人的には、ドキュメントは通常Applicationをサブクラス化する必要はないと言っていると間違っていたと思います。私は反対が真だと思います:あなたは常にアプリケーションをサブクラス化すべきです


3

アプリケーションコンテキストを使用して、コンストラクターでシステムサービスを取得します。これにより、テストが容易になり、合成のメリットが得られます

public class MyActivity extends Activity {

    private final NotificationManager notificationManager;

    public MyActivity() {
       this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
    }

    public MyActivity(NotificationManager notificationManager) {
       this.notificationManager = notificationManager;
    }

    // onCreate etc

}

テストクラスは、オーバーロードされたコンストラクタを使用します。

Androidはデフォルトのコンストラクターを使用します。


1

私はそれが好きですが、代わりにシングルトンを提案します:

package com.mobidrone;

import android.app.Application;
import android.content.Context;

public class ApplicationContext extends Application
{
    private static ApplicationContext instance = null;

    private ApplicationContext()
    {
        instance = this;
    }

    public static Context getInstance()
    {
        if (null == instance)
        {
            instance = new ApplicationContext();
        }

        return instance;
    }
}

31
android.app.applicationを拡張するとシングルトンが保証されるため、これは不要です
Vincent

8
非活動クラスからのアクセスが必要な場合はどうなりますか?
Maxrunner

9
newアプリケーションを自分で絶対に使用しないでください(単体テストの例外を除く)。オペレーティングシステムはそれを行います。コンストラクタも必要ありません。それがonCreate目的です。
マーティン

@Vincent:これにリンクを投稿できますか?好ましくは、コード-私はここに求めています:stackoverflow.com/questions/19365797/...
Mr_and_Mrs_D

@radzioなぜコンストラクタでそれを行うべきではないのですか?
Miha_x64 2017年

1

私は同じアプローチを使用していますが、シングルトンをもう少し上手に書くことをお勧めします:

public static MyApp getInstance() {

    if (instance == null) {
        synchronized (MyApp.class) {
            if (instance == null) {
                instance = new MyApp ();
            }
        }
    }

    return instance;
}

私は、どこにでも私の使用を使用していないgetContext()getApplicationContext()どこ私はそれを行うことができます!


だから、私が理解できるように、なぜあなたが答えに反対票を投じたのかを説明するコメントを書いてください。シングルトンアプローチは、アクティビティの外部で有効なコンテキストを取得したり、ボディを表示したりするために広く使用されています...
セラフィムの

1
オペレーティングシステムは、アプリケーションが1回だけインスタンス化されることを保証するため、必要ありません。もしあれば、SingeltonをonCreate()に設定することをお勧めします。
マーティン

1
シングルトンを遅延初期化するスレッドセーフな方法ですが、ここでは必要ありません。
naXa 14

2
うわー、私は思ったちょうどその時、人々は最終的に...ダブルチェックロックを使用して停止していたcs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
セーレンBoisen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.