コンテキストを取得するためのさまざまな方法の違いは何ですか?
-
06-07-2019 - |
質問
私が見たさまざまなAndroidコードの一部:
public class MyActivity extends Activity {
public void method() {
mContext = this; // since Activity extends Context
mContext = getApplicationContext();
mContext = getBaseContext();
}
}
ただし、どの状況が望ましいか、どのような状況で使用されるべきかについてのきちんとした説明は見つかりません。
これに関するドキュメントへのポインタ、および間違ったものが選択された場合に何が壊れるかについてのガイダンスは大歓迎です。
解決
Androidのコンテキストに関してはドキュメントがまばらであることに同意しますが、さまざまなソースからいくつかの事実をまとめることができます。
このブログ投稿は、公式のGoogle Androidに掲載されています開発者のブログは主にメモリリークに対処するために書かれましたが、コンテキストに関するいくつかの良い情報も提供しています:
通常のAndroidアプリケーションでは、 通常、2種類のコンテキストがあります。 アクティビティとアプリケーション。
この記事を少し読むと、2つの違いと、アクティビティコンテキスト Activity.getApplicationContext()
)を使用することを検討する場合の違いがわかります。 > this )。基本的に、アプリケーションコンテキストはアプリケーションに関連付けられ、アプリのライフサイクルを通じて常に同じです。アクティビティコンテキストはアクティビティに関連付けられ、画面の向きの変更中にアクティビティが破棄されると何度も破棄される可能性があるため、など。
Android SDKに取り組んでいるGoogleエンジニアの1人であるDianne Hackbornからの投稿以外に、getBaseContext()をいつ使用するかについて本当に何も見つかりませんでした:
getBaseContext()を使用せず、単に使用する 持っているコンテキスト。
android-developersニュースグループでは、Androidで作業している少数の人々が実際にニュースグループを監視し、質問に答えているため、そこで質問することを検討することもできます。
したがって、全体的には可能な場合はグローバルアプリケーションコンテキストを使用することをお勧めします。
他のヒント
context
の使用に関して私が見つけたものは次のとおりです。
1)。 Activity
自体の中では、レイアウトとメニューの拡張、コンテキストメニューの登録、ウィジェットのインスタンス化、他のアクティビティの開始に this
を使用します、 Activity
内に新しい Intent
を作成し、プリファレンスをインスタンス化するか、 Activity
で使用可能な他のメソッドを作成します。
レイアウトを展開する:
View mView = this.getLayoutInflater().inflate(R.layout.myLayout, myViewGroup);
膨張メニュー:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
this.getMenuInflater().inflate(R.menu.mymenu, menu);
return true;
}
コンテキストメニューの登録:
this.registerForContextMenu(myView);
インスタンス化ウィジェット:
TextView myTextView = (TextView) this.findViewById(R.id.myTextView);
アクティビティ
を開始します:
Intent mIntent = new Intent(this, MyActivity.class);
this.startActivity(mIntent);
設定のインスタンス化:
SharedPreferences mSharedPreferences = this.getPreferenceManager().getSharedPreferences();
2)。アプリケーション全体のクラスの場合、このコンテキストはアプリケーションの存続期間中に存在するため、 getApplicationContext()
を使用します。
現在のAndroidパッケージの名前を取得します:
public class MyApplication extends Application {
public static String getPackageName() {
String packageName = null;
try {
PackageInfo mPackageInfo = getApplicationContext().getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), 0);
packageName = mPackageInfo.packageName;
} catch (NameNotFoundException e) {
// Log error here.
}
return packageName;
}
}
アプリケーション全体のクラスをバインドします:
Intent mIntent = new Intent(this, MyPersistent.class);
MyServiceConnection mServiceConnection = new MyServiceConnection();
if (mServiceConnection != null) {
getApplicationContext().bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
3)。リスナーおよびその他のタイプのAndroidクラス(ContentObserverなど)には、次のようなコンテキスト置換を使用します。
mContext = this; // Example 1
mContext = context; // Example 2
this
または context
はクラスのコンテキスト(アクティビティなど)です。
アクティビティ
コンテキスト置換:
public class MyActivity extends Activity {
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
}
}
リスナーコンテキストの置換:
public class MyLocationListener implements LocationListener {
private Context mContext;
public MyLocationListener(Context context) {
mContext = context;
}
}
ContentObserver
コンテキスト置換:
public class MyContentObserver extends ContentObserver {
private Context mContext;
public MyContentObserver(Handler handler, Context context) {
super(handler);
mContext = context;
}
}
4)。 BroadcastReceiver
(インライン/埋め込みレシーバーを含む)には、レシーバーの独自のコンテキストを使用します。
外部 BroadcastReceiver
:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_SCREEN_OFF)) {
sendReceiverAction(context, true);
}
private static void sendReceiverAction(Context context, boolean state) {
Intent mIntent = new Intent(context.getClass().getName() + "." + context.getString(R.string.receiver_action));
mIntent.putExtra("extra", state);
context.sendBroadcast(mIntent, null);
}
}
}
インライン/埋め込み BroadcastReceiver
:
public class MyActivity extends Activity {
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final boolean connected = intent.getBooleanExtra(context.getString(R.string.connected), false);
if (connected) {
// Do something.
}
}
};
}
5)。サービスの場合、サービスの独自のコンテキストを使用します。
public class MyService extends Service {
private BroadcastReceiver mBroadcastReceiver;
@Override
public void onCreate() {
super.onCreate();
registerReceiver();
}
private void registerReceiver() {
IntentFilter mIntentFilter = new IntentFilter();
mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
this.mBroadcastReceiver = new MyBroadcastReceiver();
this.registerReceiver(this.mBroadcastReceiver, mIntentFilter);
}
}
6)。 Toastの場合、通常は getApplicationContext()
を使用しますが、可能であれば、アクティビティ、サービスなどから渡されたコンテキストを使用します。
アプリケーションのコンテキストを使用:
Toast mToast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG);
mToast.show();
ソースから渡されたコンテキストを使用:
public static void showLongToast(Context context, String message) {
if (context != null && message != null) {
Toast mToast = Toast.makeText(context, message, Toast.LENGTH_LONG);
mToast.show();
}
}
そして最後に、Androidのフレームワーク開発者のアドバイスに従って getBaseContext()
を使用しないでください。
更新: Context
の使用例を追加します。
数日前にこのスレッドを読んで、同じ質問をしました。これを読んだ後の私の決定は簡単でした。常にapplicationContextを使用します。
しかし、これで問題が発生し、それを見つけるのに数時間を費やし、それを解決するのに数秒を費やしました...(一語を変える...)
Spinnerを含むビューを展開するためにLayoutInflaterを使用しています。
したがって、2つの可能性があります。
1)
LayoutInflater layoutInflater = LayoutInflater.from(this.getApplicationContext());
2)
LayoutInflater layoutInflater = LayoutInflater.from(this.getBaseContext());
その後、私はこのようなことをしています:
// managing views part
View view = ContactViewer.mLayoutInflater.inflate(R.layout.aViewContainingASpinner, theParentView, false);
Spinner spinner = (Spinner) view.findViewById(R.id.theSpinnerId);
String[] myStringArray = new String[] {"sweet","love"};
// managing adapter part
// The context used here don't have any importance -- both work.
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this.getApplicationContext(), myStringArray, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
theParentView.addView(view);
私が気づいたこと:applicationContextでlinearLayoutをインスタンス化した場合、アクティビティでスピナーをクリックすると、dalvik仮想マシンからのキャッチされない例外が発生します(コードからではなく、私の間違いがどこにあるかを見つけるのに多くの時間を費やしました...)。
baseContextを使用する場合、それで問題ありません。コンテキストメニューが開き、選択項目から選択できます。
だからここに私の結論があります:アクティビティでcontextMenuを処理するときにbaseContextが必要だと思います(それ以上テストしていません)...
テストはAPI 8でコーディングされ、HTC Desire、Android 2.3.3でテストされました。
これまでの私のコメントがあなたを退屈させないことを願っています。ハッピーコーディング;-)
まず、可能な限りappcontextを使用することに同意します。その後、「これ」活動中。 basecontextは必要ありませんでした。
私のテストでは、ほとんどの場合、交換できます。ほとんどの場合、コンテキストを取得する理由は、ファイル、設定、データベースなどにアクセスすることです。これらのデータは、最終的にアプリのプライベートデータフォルダー(/ data / data /)のファイルとして反映されます。使用するコンテキストに関係なく、それらは同じフォルダ/ファイルにマップされるため、問題ありません。
それは私が観察したことです。たぶんそれらを区別する必要がある場合があります。
場合によっては、スレッドで何かを実行するときに、アプリケーションコンテキストではなくアクティビティコンテキストを使用できます。スレッドの実行が完了し、結果を呼び出し側アクティビティに戻す必要がある場合、ハンドラーを使用してそのコンテキストが必要になります。
((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
簡単な言葉で
メソッド名が示唆する getApplicationContext()
は、アプリ内のどこからでもアクセスできるアプリケーション全体の詳細をアプリに認識させます。したがって、これをサービスバインディング、ブロードキャスト登録などで使用できます。アプリケーションコンテキスト
は、アプリが終了するまで有効です。
getActivity()
または this
は、 application context
によって提供されるアプリレベルの詳細も表示される現在の画面をアプリに認識させます。 。したがって、 Window
ActionBar
Fragementmanger
など、現在の画面について知りたいものはすべてこのコンテキストで使用できます。基本的に、 Activity
は Context
を拡張します。このコンテキストは、現在のコンポーネント(アクティビティ)が有効になるまで有効です
onClick
からトーストするときに、これと getBaseContext
のみを使用しました(JavaとAndroidの両方に非常に緑色のnoob)。私はクリッカーがアクティビティに直接あるときにこれを使用し、匿名の内部クリッカーで getBaseContext
を使用する必要があります。これは getBaseContext
のトリックだと思います。おそらく、内部クラスが隠れているアクティビティのコンテキストを返しているのでしょう。
この混乱は、次のような多くの方法があるという事実から生じています。 (表面上)識別可能な違いなしでコンテキストにアクセスします。 以下は、アクセスできる最も一般的な4つの方法です。 アクティビティのコンテキスト。
getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new
コンテキストとは 個人的には、Contextをいつでもアプリケーションの状態と考えることが好きです。アプリケーションコンテキストは、アプリケーションのグローバルまたは基本構成を表し、アクティビティまたはサービスはそれに基づいて構築でき、アプリケーションの構成インスタンスまたはその遷移状態を表します。
android.content.Contextのソースを見ると、Contextは抽象クラスであり、クラスのコメントは次のとおりであることがわかります。
アプリケーション環境に関するグローバル情報へのインターフェース。これは、Androidシステムによって実装が提供される抽象クラスです。それ
application-specific
リソースとクラスへのアクセスと、アクティビティの起動、インテントのブロードキャスト、受信などの application-level
操作の呼び出しを許可します。
これから私が取り上げるのは、Contextがシステムレベルのリソースだけでなくアプリケーションレベルにアクセスするための共通の実装を提供することです。アプリケーションレベルのリソースは、文字列リソース [getResources()]
またはアセット [getAssets()]
のようなものにアクセスする場合があり、システムレベルのリソースはでアクセスするものですContext.getSystemService()。
実際のところ、メソッドに関するコメントを見ると、この概念を補強しているようです:
getSystemService()
: system-level
サービスへのハンドルを名前で返します。返されるオブジェクトのクラスは、要求された名前によって異なります。
getResources()
:アプリケーションのパッケージのResourcesインスタンスを返します。
getAssets()
:アプリケーションのパッケージのResourcesインスタンスを返します。
Context抽象クラスでは、上記のすべてのメソッドが抽象であることを指摘する価値があります。 getSystemService(Class)の1つのインスタンスのみが実装を持ち、抽象メソッドを呼び出します。つまり、これらの実装は、主に以下を含む実装クラスによって提供される必要があります。
ContextWrapper
Application
Activity
Service
IntentService
APIドキュメントを見ると、クラスの階層は次のようになっています。
コンテキスト
|&#8202;&#8212;&#8202; ContextWrapper
|&#8212;&#8202;&#8212;&#8202;アプリケーション
|&#8202;&#8212;&#8202;&#8212; ContextThemeWrapper
|&#8212;&#8202;&#8212;&#8202;&#8212;&#8202;&#8212;&#8202;アクティビティ
|&#8202;&#8212;&#8202;&#8212;サービス
|&#8212;&#8202;&#8212;&#8202;&#8212; IntentService
Context
自体が洞察を提供していないことを知っているので、ツリーを下に移動して ContextWrapper
を見て、そこにはあまりないことがわかりますどちらか。 Applicationは ContextWrapper
を拡張するので、 ContextWrapper
が提供する実装をオーバーライドしないため、そこをあまり見る必要はありません。これは、Contextの実装がOSによって提供され、 API
から隠されていることを意味します。 ContextImplクラスのソースを見ると、Contextの具体的な実装を見ることができます。