Androidで「コンテキスト」を取得する静的な方法は?


970

Context静的メソッド内で現在のインスタンスを取得する方法はありますか?

変更するたびに「Context」インスタンスを保存するのが嫌なので、私はその方法を探しています。


57
コンテキストを保存しないことは、不便なだけでなく、大量のメモリリークを引き起こす可能性があるため、より良い考えです!
Vikram Bodicherla 2012

12
@VikramBodicherlaはい。ただし、以下の回答は、アプリケーションコンテキストについて話していることを前提としています。したがって、メモリリークは問題になりませんが、ユーザーはこれらのソリューションを使用する必要があります。
トム

を取得する静的な方法を使用する必要がある場合はContext、コードを設計するためのより良い方法があるかもしれません。
Anonsage 2015

3
Androidのドキュメントでは、シングルトンのゲッターにコンテキストを渡すことを推奨しています。developer.android.com/reference/android/app/Application.html
Marco Luglio 2015

静的なコンテキストよりもgetInstance()で渡されるシングルトンとコンテキストを優先する場合は、こちらをご覧ください。ここでは、実際のコードでサポートされる私の推論を説明しようとしました: stackoverflow.com/a/38967293/4469112
Alessio

回答:


1302

これを行う:

Androidマニフェストファイルで、次のように宣言します。

<application android:name="com.xyz.MyApplication">

</application>

次に、クラスを記述します。

public class MyApplication extends Application {

    private static Context context;

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

    public static Context getAppContext() {
        return MyApplication.context;
    }
}

これで、どこでも呼び出しMyApplication.getAppContext()て、アプリケーションコンテキストを静的に取得します。


81
この方法の欠点はありますか?これは不正行為のようです。(ハック?)
jjnguy

203
欠点は、一部の静的初期化コードがContextオブジェクトをフェッチしようとする前に、非静的onCreate()が呼び出されるという保証がないことです。つまり、呼び出し側のコードはnull値を処理する準備ができている必要があることを意味します。
Melinda Green、

8
また、たぶん..このstatic context変数を次のように宣言する必要がありvolatileますか?
Vladimir Sorokin

14
@Tomこれは、静的データメンバーが最初は静的である場合ではありません。指定されたコードでは、静的メンバーはonCreate()で非静的に初期化されています。この場合、静的に初期化されたデータでも十分ではありません。他のクラスの静的な初期化中にアクセスされる前に、特定のクラスの静的な初期化が行われることを保証するものは何もないからです。
Melinda Green

10
@MelindaGreenアプリケーションのドキュメントによると、アクティビティ、サービス、またはレシーバー(コンテンツプロバイダーを除く)が作成される前にonCreate()が呼び出されます。コンテンツプロバイダーからgetAppContext()にアクセスしない限り、このソリューションは安全ではないでしょうか。
Magnus W

86

アプリケーションコンテキストを取得する便利なメソッドを必要とするアプリの大部分は、拡張する独自のクラスを作成しますandroid.app.Application

ガイド

これを行うには、まず次のようにプロジェクトにクラスを作成します。

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

public class App extends Application {

    private static Application sApplication;

    public static Application getApplication() {
        return sApplication;
    }

    public static Context getContext() {
        return getApplication().getApplicationContext();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sApplication = this;
    }
}

次に、AndroidManifestで、AndroidManifest.xmlのタグにクラスの名前を指定する必要があります。

<application 
    ...
    android:name="com.example.App" >
    ...
</application>

次に、以下を使用して、任意の静的メソッドでアプリケーションコンテキストを取得できます。

public static void someMethod() {
    Context context = App.getContext();
}

警告

上記のようなものをプロジェクトに追加する前に、ドキュメントの内容を検討する必要があります。

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


反射

リフレクションを使用してアプリケーションコンテキストを取得する別の方法もあります。リフレクションはAndroidで軽視されることが多く、個人的には本番環境では使用しないでください。

アプリケーションコンテキストを取得するには、API 1以降で使用できる非表示クラス(ActivityThread)のメソッドを呼び出す必要があります。

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.ActivityThread")
            .getMethod("currentApplication").invoke(null, (Object[]) null);
}

静的な方法でアプリケーションコンテキストを取得する方法を提供するもう1つの非表示クラス(AppGlobals)があります。それは使用してコンテキストを取得するActivityThreadので、以下の方法と上記に投稿された方法との間に実際の違いはありません:

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.AppGlobals")
            .getMethod("getInitialApplication").invoke(null, (Object[]) null);
} 

幸せなコーディング!


56

アプリケーションコンテキストの取得について話していると仮定して、@ Rohit Ghatol拡張アプリケーションの提案に従って実装しました。次に何が起こったのかというと、そのようにして取得されたコンテキストが常にnullでないという保証はありません。必要なときに、それは通常、ヘルパーを初期化するか、リソースを取得する必要があるため、時間を遅らせることができません。nullケースの処理は役立ちません。だから私はドキュメントで述べられているように、私は基本的にAndroidアーキテクチャと戦っていたと理解しました

注:通常、Applicationをサブクラス化する必要はありません。ほとんどの状況で、静的シングルトンは同じ機能をよりモジュール的な方法で提供できます。シングルトンにグローバルコンテキストが必要な場合(たとえば、ブロードキャストレシーバーを登録する場合)、シングルトンのgetInstance()メソッドを呼び出すときに、Context引数としてContext.getApplicationContext()を含めます。

ダイアンハックボーンによって説明

アプリケーションが派生可能なものとして存在する唯一の理由は、1.0より前の開発中に、アプリケーション開発者の1人が、より「通常の「それらのアプリケーションモデルに、私は結局譲りました。私はその1つに譲ることを永遠に後悔します。:)

彼女はまた、この問題の解決策を提案しています。

アプリのさまざまな部分で共有できるグローバルな状態が必要な場合は、シングルトンを使用します。[...]これにより、より自然に、これらのものを管理する方法、つまりオンデマンドで初期化する方法が導かれます。

したがって、私がしたことは、アプリケーションの拡張を取り除き、コンテキストをシングルトンヘルパーのgetInstance()に直接渡し、アプリケーションコンテキストへの参照をプライベートコンストラクターに保存しました。

private static MyHelper instance;
private final Context mContext;    

private MyHelper(@NonNull Context context) {
    mContext = context.getApplicationContext();
}

public static MyHelper getInstance(@NonNull Context context) {
    synchronized(MyHelper.class) {
        if (instance == null) {
            instance = new MyHelper(context);
        }
        return instance;
    }
}

次に、呼び出し元はローカルコンテキストをヘルパーに渡します。

Helper.getInstance(myCtx).doSomething();

したがって、この質問に適切に回答するには、アプリケーションコンテキストに静的にアクセスする方法がありますが、それらはすべてお勧めできません。ローカルコンテキストをシングルトンのgetInstance()に渡すことをお勧めします。


興味のある方は、fwdブログで詳細なバージョンを読むことができます。


1
@Alessioこの方法ではメモリリークが発生しませんか
Phillip Kigenyi

2
@codephillip私はあなたが何を話しているのか理解できません。シングルトンは、ホストアクティビティではなく、渡されたアクティビティから取得したアプリケーションコンテキストを参照します。これは合法であり、メモリリークは発生しません。それが私が書いたブログの要点です。あなたが本当に正しいと思うなら、私があなたが話しているメモリリークを再現できるサンプルコードを送ってください、そうではありません。
アレッシオ2017

1
@KigenyiPhillipは正しいと思いますが、これは依然としてリソースリークを表しています。への最初の呼び出しの後に参照チャートを描きますgetInstance(ctx)。に渡されたコンテキストを介して収集されたアプリケーションコンテキストを参照するタイプのプライベートフィールドを持つinstanceタイプのGCルートがあります。 が再度設定されることも、クリアされることもないため、GCはによって参照されるappcontextをキャッチしません。アクティビティをリークしないため、低コストのIMOです。MyHelpermContextContextgetInstance()instanceinstance
マークマッケナ

1
@MarkMcKennaは、「アプリケーションコンテキストを参照する、タイプContextのプライベートフィールドmContextがあります」と述べているので、mContextは、アプリケーションコンテキストではなく、アプリケーションコンテキストへの参照であることは明らかです。getApplicationContext()ドキュメントお読み:「は、そのライフサイクルプロセスの寿命ではなく、現在のコンポーネントに接続されている現在のコンテキストから分離されたコンテキスト」。これはどのようにしてメモリリークを引き起こしますか?アプリケーションコンテキストは、プロセスが終了したときにのみGCされます。
アレッシオ2018

1
@Alessioアプリケーションコンテキストへの参照がリソースリークとして認められないことを受け入れる場合、静的参照をthisinにポストすることでこれを簡略化できApplication.onCreate()、受け入れられた回答がより適切になります。
マークマッケナ


38

これは、UIスレッドの任意の場所からアプリケーション(コンテキスト)を取得する、文書化されていない方法です。隠された静的メソッドに依存しています。少なくともAndroid 4.xで動作するはずです。ActivityThread.currentApplication()

try {
    final Class<?> activityThreadClass =
            Class.forName("android.app.ActivityThread");
    final Method method = activityThreadClass.getMethod("currentApplication");
    return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
    // handle exception
} catch (final NoSuchMethodException e) {
    // handle exception
} catch (final IllegalArgumentException e) {
    // handle exception
} catch (final IllegalAccessException e) {
    // handle exception
} catch (final InvocationTargetException e) {
    // handle exception
}

このメソッドがnullを返す可能性があることに注意してください。たとえば、UIスレッドの外部でメソッドを呼び出した場合や、アプリケーションがスレッドにバインドされていない場合などです。

アプリケーションコードを変更できる場合は、@RohitGhatolのソリューションを使用することをお勧めします。


1
上記のメソッドKennyTMを使用しましたが、メソッドがnullを返す場合があります。これに代わる他の方法はありますか?ここでnullを取得した場合と同様に、他の場所からコンテキストを取得できます。私の場合、ApplicationのonCreate()は呼び出されません。しかし、上記のメソッドはその前に呼び出されます。Plzzzヘルプ
AndroidGuy

これは、GCがアクティビティに関連するすべてのものをクリーンアップした場合に常に機能するとは限りません。
AlexVPerl 2015年

32

それは、コンテキストを何に使用しているかによって異なります。その方法には少なくとも1つの欠点があります。

AlertDialogwith を作成しようとするとAlertDialog.BuilderApplicationコンテキストは機能しません。私はあなたが現在の状況を必要とすると信じていますActivity...


6
そのとおり。そのためにアプリケーションコンテキストを使用すると、ダイアログがフォアグラウンドアクティビティの下に隠れて表示される場合があります。
ネイト

3
まずは+1。発生する可能性のあるエラーは、アクティビティを開始できませんComponentInfo {com.samples / com.MyActivity}:android.view.WindowManager $ BadTokenException:Unable to add window --Token null is not for an application
Govind

15

Kotlinの方法

マニフェスト:

<application android:name="MyApplication">

</application>

MyApplication.kt

class MyApplication: Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    companion object {
        lateinit var instance: MyApplication
            private set
    }
}

その後、次の方法でプロパティにアクセスできます MyApplication.instance


11

RoboGuiceを使用するのが好きな場合は、必要なクラスにコンテキストを挿入できます。これは、RoboGuice 2.0(この記事の執筆時点でのベータ4)を使用して行う方法の小さなサンプルです。

import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;

import javax.inject.Inject;

@ContextSingleton
public class DataManager {
    @Inject
    public DataManager(Context context) {
            Properties properties = new Properties();
            properties.load(context.getResources().getAssets().open("data.properties"));
        } catch (IOException e) {
        }
    }
}

8

私はある時点でこれを使用しました:

ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();

これは、システムサービスを取得する際に使用し、機能した有効なコンテキストです。

しかし、私はフレームワーク/ベースの変更でのみ使用し、Androidアプリケーションでは使用しませんでした。

知っておく必要のある警告:このコンテキストでブロードキャストレシーバーに登録すると、機能せず、次のようになります。

java.lang.SecurityException:指定された呼び出し元パッケージandroidがプロセスProcessRecordで実行されていません


7

コトリン

open class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        mInstance = this
    }

    companion object {
        lateinit var mInstance: MyApp
        fun getContext(): Context? {
            return mInstance.applicationContext
        }
    }
}

のようなコンテキストを取得する

MyApp.mInstance

または

MyApp.getContext()

4

次のものを使用できます。

MainActivity.this.getApplicationContext();

MainActivity.java:

...
public class MainActivity ... {
    static MainActivity ma;
...
    public void onCreate(Bundle b) {
         super...
         ma=this;
         ...

その他のクラス:

public ...
    public ANY_METHOD... {
         Context c = MainActivity.ma.getApplicationContext();

3
これは、内部クラスの内部にいる場合にのみ機能します。これは、OPではほとんど当てはまりません。
リチャードJ.ロスIII

3
これは、MainActivityが作成された後にANY_METHODが呼び出される限り機能しますが、アクティビティへの静的参照を維持すると、ほぼ必然的にメモリリークが発生します(OPの質問に対する他の応答で既に述べたように)、静的参照を維持する必要がある場合は、アプリケーションを使用してくださいコンテキストのみ。
2013年

1
内部クラスは悪です。最悪の部分は、多くのチュートリアルがAsyncTasksやそのようなことのためにそれを行うということです。なぜなら、多くのチュートリアルはそれをそのように行うからです
13:14に再継承

4

マニフェストファイルを変更したくない場合は、初期アクティビティの静的変数に手動でコンテキストを保存できます。

public class App {
    private static Context context;

    public static void setContext(Context cntxt) {
        context = cntxt;
    }

    public static Context getContext() {
        return context;
    }
}

そして、あなたの活動が始まる時のコンテキストを設定してください:

// MainActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set Context
    App.setContext(getApplicationContext());

    // Other stuff
}

注:他のすべての回答と同様に、これは潜在的なメモリリークです。


1
この場合のコンテキストはアプリケーションにバインドされているため、正確に何がリークされますか?アプリケーションが停止すると、他のすべても停止します。
TheRealChx101

3

getAppContext()メソッドの本体が必要だと思います。

public static Context getAppContext()
   return MyApplication.context; 

3

このソースによると ContextWrapperを拡張することで、独自のコンテキストを取得できます

public class SomeClass extends ContextWrapper {

    public SomeClass(Context base) {
      super(base);
    }

    public void someMethod() {
        // notice how I can use "this" for Context
        // this works because this class has it's own Context just like an Activity or Service
        startActivity(this, SomeRealActivity.class);

        //would require context too
        File cacheDir = getCacheDir();
    }
}

ContextWrapperのJavaDoc

すべての呼び出しを別のコンテキストに委任するだけのコンテキストのプロキシ実装。元のコンテキストを変更せずに動作を変更するためにサブクラス化できます。


1
これは面白い。ContextWrapperについて学ぶのは良いことです。ただし、アプリケーションコンテキストでこのコンストラクタに渡す必要がある場合でも、どこかから取得する必要があります。
jk7 2017

2

なんらかの理由で、アプリケーション/アクティビティを拡張するクラスだけでなく、何らかのクラスでアプリケーションコンテキストが必要な場合は、おそらくいくつかのファクトリクラスまたはヘルパークラスで使用します。次のシングルトンをアプリに追加できます。

public class GlobalAppContextSingleton {
    private static GlobalAppContextSingleton mInstance;
    private Context context;

    public static GlobalAppContextSingleton getInstance() {
        if (mInstance == null) mInstance = getSync();
        return mInstance;
    }

    private static synchronized GlobalAppContextSingleton getSync() {
        if (mInstance == null) mInstance = 
                new GlobalAppContextSingleton();
        return mInstance;
    }

    public void initialize(Context context) {
        this.context = context;
    }

    public Context getApplicationContext() {
        return context;
    }
}

次に、アプリケーションクラスのonCreateでそれを初期化します。

GlobalAppContextSingleton.getInstance().initialize(this);

呼び出してどこでも使用

GlobalAppContextSingleton.getInstance().getApplicationContext()

ただし、この方法はアプリケーションコンテキスト以外にはお勧めしません。メモリリークを引き起こす可能性があるため。


これは、クラス/メソッド名が適切に設定され、長く維持され、(できれば)Q&Aの説明になり、自分で使用するために短縮されたようではありません。
Versa

1

シングルトンデザインパターンのバリエーションを使用して、これを支援します。

import android.app.Activity;
import android.content.Context;

public class ApplicationContextSingleton {
    private static Activity gContext;

    public static void setContext( Activity activity) {
        gContext = activity;
    }

    public static Activity getActivity() {
        return gContext;
    }

    public static Context getContext() {
        return gContext;
    }
}

次にApplicationContextSingleton.setContext( this );activity.onCreate()およびonDestroy()ApplicationContextSingleton.setContext( null );呼び出します。


コンテキストが必要な場合は、activity.getApplicationContext();を呼び出すことができます。リークを心配することなく静的に保持できます。
MinceMan

2
これによりメモリリークが発生します
BlueWizard

1

アプリの開発を簡単にすることを目的とした、Vapor APIと呼ばれるAndroid向けのjQueryにヒントを得たフレームワークをリリースしました。

中央の$ファサードクラスは、次の呼び出しで取得できるWeakReference現在のActivityコンテキストへの(Ethan Nicholasによるこれに関する素晴らしいJavaブログ投稿へのリンク)を維持します。

$.act()

Aは、WeakReferenceあなたがメモリリークの問題を持つべきではないので、元のオブジェクトを再利用ガベージコレクションを妨げることなく参照を保持します。

もちろん欠点は、$.act()nullを返す可能性のあるリスクを冒すことです。私はまだこのシナリオに遭遇していませんので、言及する価値のある最小限のリスクにすぎないかもしれません。

クラスVaporActivityとして使用していない場合は、手動でコンテキストを設定することもできますActivity

$.act(Activity);

また、Vapor APIフレームワークの多くは、この保存されたコンテキストを本質的に使用するため、フレームワークを使用する場合、自分で保存する必要はまったくありません。詳細とサンプルについては、サイトをチェックしてください。

それが役に立てば幸いです:)


1
どうやらこれはちょうど反対票を投じたようです...説明がいいでしょう!?
ダリアス

1
私はこれに反対票を投じませんでしたが、Javascriptは当面の質問とは何の関係もありません。乾杯。
Ernani Joppert、2014

流暢なインターフェイスのようなjQueryのいくつかの側面とその抽象化に触発されているとすれば、それはかなり無意味です。これらは、基礎となる言語にとらわれない原則です!
Darius

1
同じプラットフォーム上にないフレームワークのAPIセマンティクスに触発されたため、反対票を投じているのですか。私は皆さんがプラットフォームにとらわれない原則を適用するポイントを逃していると思います.................................................. ...
ダリアス

3
この答えはJavaScriptとはまったく関係ありません。投票する前に回答をお読みください:/
BlueWizard

1

Rohitの答えは正しいようです。ただし、AndroidStudioの「インスタントラン」はstatic Context、私の知る限り、コードに属性がないことに依存していることに注意してください。


1
あなたが正しいです。また、メモリリークが発生します。
user1506104 2018

1

Kotlinでは、コンテキスト/アプリコンテキストをコンパニオンオブジェクトに配置しても警告が生成される Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

または、次のようなものを使用した場合:

    companion object {
        lateinit var instance: MyApp
    }

アプリケーションクラスとその子孫がコンテキストであるため、メモリリークを発見しないようにlintをだますだけです。Appインスタンスはメモリリークを生成する可能性があります。

または、機能インターフェイスまたは機能プロパティを使用して、アプリのコンテキストを取得できます。

オブジェクトクラスを作成するだけです。

object CoreHelper {
    lateinit var contextGetter: () -> Context
}

または、null許容型を使用してより安全に使用できます。

object CoreHelper {
    var contextGetter: (() -> Context)? = null
}

Appクラスに次の行を追加します。


class MyApp: Application() {

    override fun onCreate() {
        super.onCreate()
        CoreHelper.contextGetter = {
            this
        }
    }
}

マニフェストでアプリ名を宣言します . MyApp


    <application
            android:name=".MyApp"

コンテキストを取得する場合は、次を呼び出します。

CoreHelper.contextGetter()

// or if you use the nullable version
CoreHelper.contextGetter?.invoke()

お役に立てば幸いです。


このコアヘルパーのオブジェクトクラスは初期化され、後の段階でアクティビティ全体で使用できますか?申し訳ありませんが、kotlinは初めてです
aNdRO博士

はい、正確に。
Hayi Nukman

-1

このようなものを試してください

import androidx.appcompat.app.AppCompatActivity;  
import android.content.Context; 
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    private static Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = getApplicationContext();
    }

    public static void getContext(View view){
        Toast.makeText(context, "Got my context!",
                    Toast.LENGTH_LONG).show();    
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.