Qual è la differenza tra i vari metodi per ottenere un contesto?


390

In vari bit di codice Android ho visto:

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

Tuttavia non riesco a trovare una spiegazione decente di quale sia preferibile e in quali circostanze dovrebbe essere utilizzato.

Verranno molto apprezzati i suggerimenti per la documentazione e la guida su cosa potrebbe rompersi se viene scelto quello sbagliato.


2
Questo link potrebbe aiutarti. Passa attraverso questo ..
Aju,

Risposte:


305

Sono d'accordo che la documentazione è scarsa quando si tratta di Contesti in Android, ma puoi mettere insieme alcuni fatti da varie fonti.

Questo post sul blog ufficiale degli sviluppatori di Google Android è stato scritto principalmente per aiutare a risolvere le perdite di memoria, ma fornisce anche alcune buone informazioni sui contesti:

In una normale applicazione Android, di solito hai due tipi di contesto, attività e applicazione.

Leggendo l'articolo un po 'di più si parla della differenza tra i due e quando si potrebbe voler considerare l'uso dell'applicazione Context ( Activity.getApplicationContext()) piuttosto che usare il contesto Activity this). Fondamentalmente il contesto dell'applicazione è associato all'applicazione e sarà sempre lo stesso per tutto il ciclo di vita della tua app, dove il contesto dell'attività è associato all'attività e potrebbe essere distrutto molte volte poiché l'attività viene distrutta durante i cambiamenti di orientamento dello schermo e come.

Non riuscivo a trovare davvero nulla su quando usare getBaseContext () oltre a un post di Dianne Hackborn, uno degli ingegneri di Google che lavorava su Android SDK:

Non usare getBaseContext (), usa semplicemente il contesto che hai.

Derivato da un post sul newsgroup per sviluppatori Android , potresti prendere in considerazione l'idea di porre la tua domanda anche lì, perché una manciata di persone che lavorano su Android monitora quel newsgroup e risponde alle domande.

Quindi nel complesso sembra preferibile utilizzare il contesto dell'applicazione globale quando possibile.


13
Quando ho un'attività A che può iniziare l'attività B che, a sua volta, può riavviare A con il flag CLEAR_TOP (e possibilmente ripetere questo ciclo più volte) - quale contesto dovrei usare in questo caso per evitare di costruire una scia enorme di contesti di riferimento? Diana dice di usare 'this' piuttosto che getBaseContext, ma poi ... la maggior parte delle volte A verrà riutilizzato ma ci sono situazioni in cui un nuovo oggetto per A verrà creato e poi le vecchie perdite. Quindi sembra che getBaseContext sia la scelta più corretta per la maggior parte dei casi. Quindi non è chiaro il perché Don't use getBaseContext(). Qualcuno potrebbe chiarire questo?
JBM,

2
come si accede all'oggetto contesto all'interno di una classe che non estende l'attività?
Cole,

1
@Cole, potresti creare una classe, che qui chiameremo "ExampleClass", il cui costruttore prende un oggetto Context e crea un'istanza di una variabile di istanza di classe, "appContext". Quindi, la tua classe Activity (o qualsiasi altra classe per quella materia) può chiamare un metodo ExampleClass che utilizza la variabile di istanza "appContext" di ExampleClass.
Archie1986,

54

Ecco cosa ho trovato sull'uso di context:

1). All'interno di un Activitysé, l'uso thisper gonfiare i layout ei menu, registrare i menu contestuali, istanziare i widget, avviare altre attività, creare nuovi Intentall'interno di una Activity, le preferenze istanziazione, o altri metodi disponibili in un Activity.

Gonfia layout:

View mView = this.getLayoutInflater().inflate(R.layout.myLayout, myViewGroup);

Gonfia menu:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    this.getMenuInflater().inflate(R.menu.mymenu, menu);
    return true;
}

Registra il menu contestuale:

this.registerForContextMenu(myView);

Widget di istanza:

TextView myTextView = (TextView) this.findViewById(R.id.myTextView);

Inizia un Activity:

Intent mIntent = new Intent(this, MyActivity.class);
this.startActivity(mIntent);

Preferenze di istanza:

SharedPreferences mSharedPreferences = this.getPreferenceManager().getSharedPreferences();

2). Per la classe a livello di applicazione, utilizzare getApplicationContext()poiché questo contesto esiste per la durata dell'applicazione.

Recupera il nome dell'attuale pacchetto 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;
    }
}

Associare una classe a livello di applicazione:

Intent mIntent = new Intent(this, MyPersistent.class);
MyServiceConnection mServiceConnection = new MyServiceConnection();
if (mServiceConnection != null) {
    getApplicationContext().bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}

3). Per gli ascoltatori e altri tipi di lezioni Android (ad es. ContentObserver), utilizzare una sostituzione di contesto come:

mContext = this;    // Example 1
mContext = context; // Example 2

dove thiso contextè il contesto di una classe (attività, ecc.).

Activity sostituzione del contesto:

public class MyActivity extends Activity {
    private Context mContext;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        mContext = this;
    }
}

Sostituzione del contesto dell'ascoltatore:

public class MyLocationListener implements LocationListener {
    private Context mContext;
    public MyLocationListener(Context context) {
        mContext = context;
    }
}

ContentObserver sostituzione del contesto:

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

4). Per BroadcastReceiver(incluso il ricevitore incorporato / incorporato), utilizzare il proprio contesto del destinatario.

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

Inline / Embedded 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). Per i Servizi, utilizzare il proprio contesto del servizio.

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). Per i toast, usa generalmente getApplicationContext(), ma dove possibile, usa il contesto passato da un'attività, un servizio, ecc.

Usa il contesto dell'applicazione:

Toast mToast = Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG);
mToast.show();

Usa contesto passato da una fonte:

public static void showLongToast(Context context, String message) {
    if (context != null && message != null) {
        Toast mToast = Toast.makeText(context, message, Toast.LENGTH_LONG);
        mToast.show();
    }
}

E infine, non utilizzare getBaseContext()come consigliato dagli sviluppatori del framework Android.

AGGIORNAMENTO: aggiungi esempi di Contextutilizzo.


1
Invece di mContext si può usare OuterClass.this; vedere i commenti a stackoverflow.com/questions/9605459/...
Paul Verest

3
+1 per una risposta così utile! Sono d'accordo che la risposta accettata va bene come risposta accettata, ma santo cielo questa risposta è stata super istruttiva! Grazie per tutti quegli esempi, mi hanno aiutato a capire meglio l'utilizzo del contesto nel suo insieme. Ho anche copiato la tua risposta in un file di testo sulla mia macchina come riferimento.
Ryan,

13

Ho letto questa discussione qualche giorno fa, ponendomi la stessa domanda. La mia decisione dopo aver letto questo è stata semplice: utilizzare sempre applicationContext.

Tuttavia, ho riscontrato un problema con questo, ho trascorso alcune ore per trovarlo e alcuni secondi per risolverlo ... (cambiando una parola ...)

Sto usando un LayoutInflater per gonfiare una vista contenente uno Spinner.

Quindi ecco due possibilità:

1)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getApplicationContext());

2)

    LayoutInflater layoutInflater = LayoutInflater.from(this.getBaseContext());

Quindi, sto facendo qualcosa del genere:

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

Cosa ho notato: se hai istanziato il tuo linearLayout con applicationContext, quindi quando fai clic sullo spinner nella tua attività, avrai un'eccezione non rilevata, proveniente dalla macchina virtuale dalvik (non dal tuo codice, ecco perché ho speso molto di tempo per scoprire dov'era il mio errore ...).

Se usi baseContext, va bene, si aprirà il menu contestuale e sarai in grado di scegliere tra le tue scelte.

Quindi ecco la mia conclusione: suppongo (non l'ho testato ulteriormente) di quanto sia richiesto il baseContext quando si tratta di contextMenu nella tua attività ...

Il test è stato eseguito con la codifica con API 8 e testato su un HTC Desire, Android 2.3.3.

Spero che il mio commento non ti abbia annoiato finora e ti auguro tutto il meglio. Buona codifica ;-)


Ho sempre usato "questo" durante la creazione di viste in un'attività. Sulla base del fatto che se l'attività viene riavviata, le viste vengono rifatte e potrebbe esserci un nuovo contesto da utilizzare per creare nuovamente le viste. Lo svantaggio pubblicato nel blog degli sviluppatori è che mentre ImageView è distrutto, il drawable / bitmap utilizzato potrebbe bloccarsi in quel contesto. Tuttavia è quello che faccio al momento. Per quanto riguarda il codice altrove nell'app (classi normali) uso semplicemente il contesto dell'applicazione in quanto non è specifico di alcuna attività o elemento dell'interfaccia utente.
JonWillis,

6

Innanzitutto, sono d'accordo che dovremmo usare appcontext ogni volta che è possibile. quindi "questo" in attività. non ho mai avuto bisogno di basecontext.

Nei miei test, nella maggior parte dei casi possono essere scambiati. Nella maggior parte dei casi, il motivo per cui si desidera acquisire un contesto è accedere a file, preferenze, database ecc. Questi dati vengono infine riflessi come file nella cartella dei dati privati ​​dell'app (/ data / data /). Indipendentemente dal contesto in cui utilizzi, verranno mappati sulla stessa cartella / file, quindi stai bene.

Questo è quello che ho osservato. Forse ci sono casi in cui dovresti distinguerli.


Ho bisogno di basecontext per impostare globalmente la lingua dell'app all'avvio (quando non corrisponde a quella del lang predefinito del telefono).
Tina,

3

In alcuni casi è possibile utilizzare il contesto Attività sul contesto dell'applicazione quando si esegue qualcosa in un thread. Quando il thread completa l'esecuzione e è necessario restituire il risultato all'attività del chiamante, è necessario quel contesto con un gestore.

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

2

In parole semplici

getApplicationContext()come suggerisce il nome del metodo, la tua app sarà a conoscenza di tutti i dettagli dell'applicazione a cui puoi accedere da qualsiasi parte dell'app. Quindi puoi farne uso nel collegamento al servizio, nella registrazione delle trasmissioni, ecc.Application context Sarà fino alla chiusura dell'app.

getActivity()o thisrenderà la tua app consapevole della schermata corrente che è visibile anche i dettagli a livello di app forniti da application context. Quindi, qualunque cosa tu voglia sapere sulla schermata attuale, Window ActionBar Fragementmangere così sono disponibili in questo contesto. Fondamentalmente ed Activityestendere Context. Questo contesto rimarrà attivo fino a quando l'attuale componente (attività) non sarà attivo


1

La confusione deriva dal fatto che esistono numerosi modi per accedere al contesto, con (in superficie) differenze non distinguibili. Di seguito sono riportati quattro dei modi più comuni in cui potresti essere in grado di accedere al contesto in un'attività.

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

Che cos'è un contesto? Personalmente mi piace pensare al contesto come lo stato della tua domanda in qualsiasi momento. Il contesto dell'applicazione rappresenta una configurazione globale o di base dell'applicazione e un'attività o un servizio può basarsi su di essa e rappresenta un'istanza di configurazione dell'applicazione o uno stato transitivo per essa.

Se guardi alla fonte per android.content.Context, vedi che Context è una classe astratta e i commenti sulla classe sono i seguenti:

Interfaccia con le informazioni globali su un ambiente applicativo. Questa è una classe astratta la cui implementazione è fornita dal sistema Android. Permette l'accesso a application-specificrisorse e classi, nonché inviti a application-leveloperazioni come l'avvio di attività, la trasmissione e la ricezione di intenti, ecc. Ciò che tolgo da questo è che il contesto fornisce un'implementazione comune per accedere sia a livello di applicazione che a livello di sistema risorse. Le risorse a livello di applicazione potrebbero accedere ad elementi come risorse [getResources()]o risorse String [getAssets()]e le risorse a livello di sistema sono qualsiasi cosa a cui si accedeContext.getSystemService().

È un dato di fatto, dai un'occhiata ai commenti sui metodi e sembrano rafforzare questa nozione:

getSystemService(): Restituisce l'handle a un system-levelservizio per nome. La classe dell'oggetto restituito varia in base al nome richiesto. getResources(): Restituisce un'istanza di risorse per il pacchetto dell'applicazione. getAssets(): Restituisce un'istanza di risorse per il pacchetto dell'applicazione. Potrebbe valere la pena sottolineare che nella classe astratta Context, tutti i metodi sopra indicati sono astratti! Solo un'istanza di getSystemService (Class) ha un'implementazione e che richiama un metodo astratto. Ciò significa che l'implementazione per questi dovrebbe essere fornita principalmente dalle classi di implementazione, che includono:

ContextWrapper
Application
Activity
Service
IntentService

Guardando la documentazione API, la gerarchia delle classi si presenta così:

Contesto

| - ContextWrapper

| - - Applicazione

| - - ContextThemeWrapper

| - - - - Attività

| - - Servizio

| - - - IntentService

Poiché sappiamo che di per Contextsé non fornisce alcuna intuizione, scendiamo dall'albero e diamo un'occhiata ContextWrappere ci rendiamo conto che neanche lì c'è molto. Poiché l'applicazione si estende ContextWrapper, non c'è molto da guardare anche lì, poiché non sovrascrive l'implementazione fornita da ContextWrapper. Ciò significa che l'implementazione per Context è fornita dal sistema operativo ed è nascosta da API. Puoi dare un'occhiata all'implementazione concreta di Context osservando l'origine della classe ContextImpl.


0

L'ho usato solo e getBaseContextquando brindavo da un onClicknoob molto verde a Java e Android. Lo uso quando il mio clicker è direttamente nell'attività e devo utilizzarlo getBaseContextin un clicker interno anonimo. Immagino che sia praticamente il trucco getBaseContext, forse sta restituendo il contesto dell'attività in cui si nasconde la classe interna.


1
Questo è sbagliato, sta restituendo il contesto di base dell'attività stessa. Per ottenere l'attività (quella che vuoi usare come contesto) da una classe interna anonima usa qualcosa di simile MyActivity.this. L'uso del contesto di base come descritto probabilmente non causerà problemi ma è errato.
nickmartens1980,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.