web-dev-qa-db-ja.com

コンテキストを取得するためのさまざまな方法の違いは何ですか?

私が見たAndroidコードのさまざまなビットで:

 public class MyActivity extends Activity {
    public void method() {
       mContext = this;    // since Activity extends Context
       mContext = getApplicationContext();
       mContext = getBaseContext();
    }
 }

しかし、どの状況が望ましいか、どのような状況で使用されるべきかについての適切な説明は見つかりません。

これに関するドキュメントへのポインタと、間違ったものが選択された場合に何が壊れる可能性があるかについてのガイダンスをいただければ幸いです。

380
Alnitak

Androidのコンテキストに関してはドキュメントがまばらであることに同意しますが、さまざまなソースからいくつかの事実をまとめることができます。

このブログ投稿 公式のGoogle Android開発者ブログでは、主にメモリリークに対処するために書かれましたが、コンテキストに関するいくつかの良い情報も提供しています

通常のAndroidアプリケーションでは、通常、2種類のコンテキスト、アクティビティ、およびアプリケーションがあります。

この記事を少し読むと、この2つの違いと、アクティビティコンテキスト(this)を使用するのではなく、アプリケーションコンテキスト(Activity.getApplicationContext())の使用を検討する場合の違いについて説明します。基本的に、アプリケーションコンテキストはアプリケーションに関連付けられ、アプリのライフサイクルを通じて常に同じです。アクティビティコンテキストはアクティビティに関連付けられ、画面の向きの変更中にアクティビティが破棄されると何度も破棄される可能性があるため、そのような。

Android SDKに取り組んでいるGoogleエンジニアの1人であるDianne Hackbornからの投稿以外に、getBaseContext()をいつ使用するかについて、本当に何も見つかりませんでした。

GetBaseContext()を使用せずに、持っているコンテキストを使用してください。

Android-developers newsgroup の投稿からでした。Androidで作業している少数の人々が実際にそのニュースグループを監視し、回答するため、そこで質問することを検討することもできます質問。

したがって、全体的には、可能な場合はグローバルアプリケーションコンテキストを使用することをお勧めします。

301
snctln

contextname__の使用に関して私が見つけたものは次のとおりです。

1)。Activityname__自体の中で、レイアウトとメニューの拡張、コンテキストメニューの登録、ウィジェットのインスタンス化、他のアクティビティの開始、新しいthisname__の作成にIntentname__を使用Activityname__、インスタンス化設定、またはActivityname__で使用可能な他のメソッド。

レイアウトを膨らませる:

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);

Activityname __:を開始します

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

ここで、thisname__またはcontextname__はクラスのコンテキスト(アクティビティなど)です。

Activityname__コンテキスト置換:

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;
    }
}

ContentObservername__コンテキスト置換:

public class MyContentObserver extends ContentObserver {
    private Context mContext;
    public MyContentObserver(Handler handler, Context context) {
        super(handler);
        mContext = context;
    }
}

4)。BroadcastReceivername__(インライン/埋め込みレシーバーを含む)には、レシーバー自身のコンテキストを使用します。

外部BroadcastReceivername __:

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);
        }
    }
}

インライン/埋め込みBroadcastReceivername __:

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)。トーストの場合、通常は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()を使用しないでください。

UPDATE:Contextname__の使用例を追加します。

50
ChuongPham

私は数日前にこのスレッドを読み、同じ質問を自問しました。これを読んだ後の私の決定は簡単でした。常にapplicationContextを使用します。

しかし、私はこれで問題に遭遇し、それを見つけるのに数時間を費やし、それを解決するのに数秒を費やしました...(1つの単語を変える...)

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でテストされました。

私のコメントがこれまでに退屈していないことを願っています。ハッピーコーディング;-)

12
Mav3656

まず、可能な限りappcontextを使用する必要があることに同意します。アクティビティの「this」。 basecontextは必要ありませんでした。

私のテストでは、ほとんどの場合、交換できます。ほとんどの場合、コンテキストを取得する理由は、ファイル、設定、データベースなどにアクセスすることです。これらのデータは、最終的にアプリのプライベートデータフォルダー(/ data/data /)のファイルとして反映されます。どのコンテキストを使用しても、それらは同じフォルダ/ファイルにマップされるため、問題ありません。

それが私が観察したことです。たぶんそれらを区別する必要がある場合があります。

6
samsonsu

場合によっては、スレッド内で何かを実行するときに、アプリケーションコンテキストではなくアクティビティコンテキストを使用できます。スレッドの実行が完了し、結果を呼び出し側アクティビティに返す必要がある場合、ハンドラーでそのコンテキストが必要です。

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
3
Paul

簡単な言葉で

getApplicationContext()は、メソッド名が示すように、アプリ内のどこからでもアクセスできるアプリケーション全体の詳細をアプリに認識させます。したがって、サービスバインディング、ブロードキャスト登録などでこれを利用できます。Application contextは、アプリが終了するまで有効です。

getActivity()またはthisは、application contextによって提供されるアプリレベルの詳細も表示される現在の画面をアプリに認識させます。したがって、WindowActionBarFragementmangerなど、現在の画面について知りたいことはすべて、このコンテキストで使用できます。基本的にActivityContextを拡張します。このコンテキストは、現在のコンポーネント(アクティビティ)が有効になるまで有効です

2
arjun

混乱は、コンテキストにアクセスする方法が多数あり、(表面上)識別可能な違いがないことに起因しています。以下は、アクティビティのコンテキストにアクセスできる最も一般的な4つの方法です。

getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new

コンテキストとは何ですか?私は個人的に、いつでもコンテキストをアプリケーションの状態と考えています。アプリケーションコンテキストは、アプリケーションのグローバルまたは基本構成を表し、アクティビティまたはサービスはそれに基づいて構築でき、アプリケーションの構成インスタンスまたはその遷移状態を表します。

Android.content.Contextのソースを見ると、Contextは抽象クラスであり、クラスに関するコメントは次のとおりです。

アプリケーション環境に関するグローバル情報へのインターフェース。これは、Androidシステムによって実装が提供される抽象クラスです。 application-specificリソースおよびクラスへのアクセスと、アクティビティの起動、インテントのブロードキャストおよび受信などのapplication-level操作のアップコールが可能になります。アプリケーションレベルおよびシステムレベルのリソースにアクセスするための実装。アプリケーションレベルのリソースは、文字列リソース[getResources()]またはアセット[getAssets()]のようなものにアクセスしている可能性があり、システムレベルのリソースは、Context.getSystemService().でアクセスするものです

実際問題として、メソッドに関するコメントを見てください。これらはこの概念を補強しているようです:

getSystemService():名前でsystem-levelサービスへのハンドルを返します。返されるオブジェクトのクラスは、要求された名前によって異なります。 getResources():アプリケーションのパッケージのリソースインスタンスを返します。 getAssets():アプリケーションのパッケージのリソースインスタンスを返します。 Context抽象クラスでは、上記のすべてのメソッドが抽象であることを指摘する価値があります。 getSystemService(Class)の1つのインスタンスのみが実装を持ち、抽象メソッドを呼び出します。つまり、これらの実装は、主に以下を含む実装クラスによって提供される必要があります。

ContextWrapper
Application
Activity
Service
IntentService

APIドキュメントを見ると、クラスの階層は次のようになっています。

コンテキスト

| — ContextWrapper

| — —アプリケーション

| — — ContextThemeWrapper

| — — — —アクティビティ

| — —サービス

| — — — IntentService

Context自体が洞察を提供していないことがわかっているので、ツリーを下に移動してContextWrapperを見て、そこにもあまりないことがわかります。 ApplicationはContextWrapperを拡張するので、ContextWrapperによって提供される実装をオーバーライドしないため、そこをあまり見る必要もありません。これは、Contextの実装がOSによって提供され、APIから隠されていることを意味します。 ContextImplクラスのソースを見ると、Contextの具体的な実装を見ることができます。

0
ORBIT

getBaseContext(JavaとAndroidの両方に非常に緑色のnoob)からトーストするときに、これとonClickのみを使用しました。私はクリッカーがアクティビティに直接あるときにこれを使用し、匿名の内部クリッカーでgetBaseContextを使用する必要があります。それはgetBaseContextのトリックだと思いますが、おそらく内部クラスが隠れているアクティビティのコンテキストを返しているのでしょう。

0
Tony