Context
静的メソッド内で現在のインスタンスを取得する方法はありますか?
変更するたびに「Context」インスタンスを保存するのが嫌なので、私はその方法を探しています。
Context
、コードを設計するためのより良い方法があるかもしれません。
Context
静的メソッド内で現在のインスタンスを取得する方法はありますか?
変更するたびに「Context」インスタンスを保存するのが嫌なので、私はその方法を探しています。
Context
、コードを設計するためのより良い方法があるかもしれません。
回答:
これを行う:
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()
て、アプリケーションコンテキストを静的に取得します。
static context
変数を次のように宣言する必要がありvolatile
ますか?
アプリケーションコンテキストを取得する便利なメソッドを必要とするアプリの大部分は、拡張する独自のクラスを作成します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);
}
幸せなコーディング!
アプリケーションコンテキストの取得について話していると仮定して、@ 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ブログで詳細なバージョンを読むことができます。
getInstance(ctx)
。に渡されたコンテキストを介して収集されたアプリケーションコンテキストを参照するタイプのプライベートフィールドを持つinstance
タイプのGCルートがあります。 が再度設定されることも、クリアされることもないため、GCはによって参照されるappcontextをキャッチしません。アクティビティをリークしないため、低コストのIMOです。MyHelper
mContext
Context
getInstance()
instance
instance
this
inにポストすることでこれを簡略化できApplication.onCreate()
、受け入れられた回答がより適切になります。
これは、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つの欠点があります。
AlertDialog
with を作成しようとするとAlertDialog.Builder
、Application
コンテキストは機能しません。私はあなたが現在の状況を必要とすると信じていますActivity
...
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
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) {
}
}
}
私はある時点でこれを使用しました:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
これは、システムサービスを取得する際に使用し、機能した有効なコンテキストです。
しかし、私はフレームワーク/ベースの変更でのみ使用し、Androidアプリケーションでは使用しませんでした。
知っておく必要のある警告:このコンテキストでブロードキャストレシーバーに登録すると、機能せず、次のようになります。
java.lang.SecurityException:指定された呼び出し元パッケージandroidがプロセスProcessRecordで実行されていません
次のものを使用できます。
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();
マニフェストファイルを変更したくない場合は、初期アクティビティの静的変数に手動でコンテキストを保存できます。
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
}
注:他のすべての回答と同様に、これは潜在的なメモリリークです。
このソースによると、 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();
}
}
すべての呼び出しを別のコンテキストに委任するだけのコンテキストのプロキシ実装。元のコンテキストを変更せずに動作を変更するためにサブクラス化できます。
なんらかの理由で、アプリケーション/アクティビティを拡張するクラスだけでなく、何らかのクラスでアプリケーションコンテキストが必要な場合は、おそらくいくつかのファクトリクラスまたはヘルパークラスで使用します。次のシングルトンをアプリに追加できます。
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()
ただし、この方法はアプリケーションコンテキスト以外にはお勧めしません。メモリリークを引き起こす可能性があるため。
シングルトンデザインパターンのバリエーションを使用して、これを支援します。
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 );
を呼び出します。
アプリの開発を簡単にすることを目的とした、Vapor APIと呼ばれるAndroid向けのjQueryにヒントを得たフレームワークをリリースしました。
中央の$
ファサードクラスは、次の呼び出しで取得できるWeakReference
現在のActivity
コンテキストへの(Ethan Nicholasによるこれに関する素晴らしいJavaブログ投稿へのリンク)を維持します。
$.act()
Aは、WeakReference
あなたがメモリリークの問題を持つべきではないので、元のオブジェクトを再利用ガベージコレクションを妨げることなく参照を保持します。
もちろん欠点は、$.act()
nullを返す可能性のあるリスクを冒すことです。私はまだこのシナリオに遭遇していませんので、言及する価値のある最小限のリスクにすぎないかもしれません。
クラスVaporActivity
として使用していない場合は、手動でコンテキストを設定することもできますActivity
。
$.act(Activity);
また、Vapor APIフレームワークの多くは、この保存されたコンテキストを本質的に使用するため、フレームワークを使用する場合、自分で保存する必要はまったくありません。詳細とサンプルについては、サイトをチェックしてください。
それが役に立てば幸いです:)
Rohitの答えは正しいようです。ただし、AndroidStudioの「インスタントラン」はstatic Context
、私の知る限り、コードに属性がないことに依存していることに注意してください。
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()
お役に立てば幸いです。
このようなものを試してください
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(); } }