Quelle est la différence entre les différentes méthodes pour obtenir un contexte?

390

Dans divers morceaux de code Android, j'ai vu:

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

Cependant, je ne trouve aucune explication décente de ce qui est préférable, et dans quelles circonstances qui devraient être utilisées.

Des conseils sur la documentation à ce sujet et des conseils sur ce qui pourrait se casser si le mauvais est choisi seraient très appréciés.

Alnitak
la source
2
Ce lien pourrait vous aider. Passez par ..
Aju

Réponses:

305

Je conviens que la documentation est rare en ce qui concerne les contextes dans Android, mais vous pouvez rassembler quelques faits provenant de diverses sources.

Ce billet de blog sur le blog officiel des développeurs Google Android a été écrit principalement pour aider à résoudre les fuites de mémoire, mais fournit également de bonnes informations sur les contextes:

Dans une application Android classique, vous disposez généralement de deux types de contexte, d'activité et d'application.

En lisant l'article un peu plus loin, vous découvrirez la différence entre les deux et quand vous voudrez peut-être envisager d'utiliser l'application Context ( Activity.getApplicationContext()) plutôt que d'utiliser le contexte Activity this). Fondamentalement, le contexte de l'application est associé à l'application et sera toujours le même tout au long du cycle de vie de votre application, alors que le contexte d'activité est associé à l'activité et peut éventuellement être détruit plusieurs fois car l'activité est détruite lors des changements d'orientation de l'écran et tel.

Je ne pouvais pas vraiment trouver quoi que ce soit sur l'utilisation de getBaseContext () autre qu'un article de Dianne Hackborn, l'une des ingénieurs de Google travaillant sur le SDK Android:

N'utilisez pas getBaseContext (), utilisez simplement le contexte que vous avez.

Cela provenait d'un message sur le groupe de discussion des développeurs Android , vous voudrez peut-être également y poser votre question, car une poignée de personnes travaillant sur Android surveillent ce groupe de discussion et répondent aux questions.

Donc, dans l'ensemble, il semble préférable d'utiliser le contexte d'application global lorsque cela est possible.

snctln
la source
13
Lorsque j'ai une activité A qui peut démarrer l'activité B qui, à son tour, peut redémarrer A avec l'indicateur CLEAR_TOP (et éventuellement répéter ce cycle plusieurs fois) - quel contexte dois-je utiliser dans ce cas afin d'éviter de créer une énorme traînée de contextes référencés? Diana dit d'utiliser 'this' plutôt que getBaseContext, mais alors ... la plupart du temps A sera réutilisé mais il y a des situations où un nouvel objet pour A sera créé et ensuite l'ancien A fuira. Il semble donc que getBaseContext soit le choix le plus approprié pour la plupart des cas. Ensuite, on ne sait pas pourquoi Don't use getBaseContext(). Quelqu'un pourrait-il clarifier cela?
JBM
2
comment accéder à l'objet contextuel à l'intérieur d'une classe qui n'étend pas Activity?
Cole
1
@Cole, vous pouvez créer une classe, que nous appellerons ici "ExampleClass", dont le constructeur prend un objet Context et instancie une variable d'instance de classe, "appContext". Ensuite, votre classe Activity (ou toute autre classe d'ailleurs) peut appeler une méthode ExampleClass qui utilise la variable d'instance "appContext" de ExampleClass.
Archie1986
54

Voici ce que j'ai trouvé concernant l'utilisation de context:

1) . Dans un Activitylui-même, utilisez thispour gonfler des dispositions et des menus, enregistrer des menus contextuels, instancier des widgets, démarrer d'autres activités, créer de nouveaux Intentdans un Activity, instancier des préférences ou d'autres méthodes disponibles dans un Activity.

Gonfler la disposition:

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

Gonfler le menu:

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

Enregistrer le menu contextuel:

this.registerForContextMenu(myView);

Widget d'instanciation:

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

Lancer un Activity:

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

Instancier les préférences:

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

2). Pour la classe à l'échelle de l'application, utilisez getApplicationContext()ce contexte pour la durée de vie de l'application.

Récupérez le nom du package Android actuel:

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

Liez une classe à l'échelle de l'application:

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

3). Pour les écouteurs et autres types de classes Android (par exemple ContentObserver), utilisez une substitution de contexte comme:

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

thisou contextest le contexte d'une classe (Activité, etc.).

Activity substitution de contexte:

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

Substitution du contexte d'écoute:

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

ContentObserver substitution de contexte:

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

4). Pour BroadcastReceiver(y compris le récepteur intégré / intégré), utilisez le propre contexte du récepteur.

Externe 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). Pour les services, utilisez le propre contexte du service.

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). Pour Toasts, utilisez généralement getApplicationContext(), mais si possible, utilisez le contexte transmis à partir d'une activité, d'un service, etc.

Utilisez le contexte de l'application:

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

Utilisez le contexte transmis depuis une source:

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

Et enfin, n'utilisez pas getBaseContext()comme conseillé par les développeurs de framework Android.

MISE À JOUR: Ajoutez des exemples d' Contextutilisation.

ChuongPham
la source
1
Au lieu de mContext, on peut utiliser OuterClass.this; voir les commentaires dans stackoverflow.com/questions/9605459/…
Paul Verest
3
+1 pour une réponse si utile! Je suis d'accord que la réponse acceptée est très bien comme réponse acceptée, mais sainte molly, cette réponse était super informative! Merci pour tous ces exemples, ils m'ont aidé à mieux comprendre l'utilisation du contexte dans son ensemble. J'ai même copié votre réponse dans un fichier texte sur ma machine comme référence.
Ryan
13

J'ai lu ce fil il y a quelques jours en me posant la même question. Ma décision après avoir lu ceci était simple: utilisez toujours applicationContext.

Cependant, j'ai rencontré un problème avec cela, j'ai passé quelques heures pour le trouver, et quelques secondes pour le résoudre ... (changer un mot ...)

J'utilise un LayoutInflater pour gonfler une vue contenant un Spinner.

Voici donc deux possibilités:

1)

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

2)

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

Ensuite, je fais quelque chose comme ça:

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

Ce que j'ai remarqué: si vous avez instancié votre linearLayout avec l'applicationContext, alors lorsque vous cliquez sur le spinner dans votre activité, vous aurez une exception non interceptée, provenant de la machine virtuelle dalvik (pas de votre code, c'est pourquoi j'ai dépensé beaucoup de temps pour trouver où était mon erreur ...).

Si vous utilisez le baseContext, alors ça va, le menu contextuel s'ouvrira et vous pourrez choisir parmi vos choix.

Voici donc ma conclusion: je suppose (je ne l'ai pas testé plus loin) que le baseContext est requis pour traiter contextMenu dans votre Activity ...

Le test a été fait en codant avec l'API 8, et testé sur un HTC Desire, android 2.3.3.

J'espère que mon commentaire ne vous a pas ennuyé jusqu'à présent et je vous souhaite le meilleur. Bon codage ;-)

Mav3656
la source
J'ai toujours utilisé "ceci" lors de la création de vues dans une activité. En partant du principe que si l'activité redémarre, les vues sont refaites et il y a peut-être un nouveau contexte à utiliser pour refaire les vues. L'inconvénient tel que publié dans le blog des développeurs est que lorsqu'une ImageView est détruite, le dessin / bitmap utilisé peut s'accrocher à ce contexte. C'est pourtant ce que je fais en ce moment. En ce qui concerne le code ailleurs dans l'application (classes normales), j'utilise simplement le contexte d'application car il n'est pas spécifique à une activité ou à des éléments d'interface utilisateur.
JonWillis
6

Tout d'abord, je conviens que nous devrions utiliser appcontext chaque fois que possible. puis "ceci" en activité. je n'ai jamais eu besoin de contexte de base.

Dans mes tests, dans la plupart des cas, ils peuvent être échangés. Dans la plupart des cas, la raison pour laquelle vous souhaitez saisir un contexte est d'accéder aux fichiers, aux préférences, à la base de données, etc. Ces données sont finalement reflétées sous forme de fichiers dans le dossier de données privé de votre application (/ data / data /). Quel que soit le contexte que vous utilisez, ils seront mappés sur le même dossier / fichier, donc vous êtes OK.

Voilà ce que j'ai observé. Il y a peut-être des cas où vous devriez les distinguer.

samsonsu
la source
J'ai eu besoin de basecontext pour définir globalement la langue de l'application au démarrage (quand elle ne correspond pas à celle de la langue par défaut du téléphone).
Tina
3

Dans certains cas, vous pouvez utiliser le contexte d'activité sur le contexte d'application lors de l'exécution de quelque chose dans un thread. Lorsque le thread termine l'exécution et que vous devez renvoyer le résultat à l'activité de l'appelant, vous avez besoin de ce contexte avec un gestionnaire.

((YourActivity) context).yourCallbackMethod(yourResultFromThread, ...);
Paul
la source
2

En termes simples

getApplicationContext()comme le suggère le nom de la méthode, votre application sera informée des détails de l'application auxquels vous pouvez accéder depuis n'importe où dans l'application. Ainsi, vous pouvez utiliser cela dans la liaison de service, l'enregistrement de la diffusion, etc. Application contextsera vivant jusqu'à la fin de l'application.

getActivity()ou thisrendra votre application consciente de l'écran actuel, qui est également visible, ainsi que les détails du niveau d'application fournis par application context. Donc, tout ce que vous voulez savoir sur l'écran actuel Window ActionBar Fragementmangerest disponible dans ce contexte. Fondamentalement et Activityétendre Context. Ce contexte sera vivant jusqu'à ce que le composant (activité) actuel soit vivant

arjun
la source
1

La confusion provient du fait qu'il existe de nombreuses façons d'accéder au contexte, sans (en surface) aucune différence perceptible. Vous trouverez ci-dessous quatre des moyens les plus courants d'accéder au contexte dans une activité.

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

Qu'est-ce qu'un contexte? Personnellement, j'aime considérer le contexte comme l'état de votre demande à tout moment. Le contexte d'application représente une configuration globale ou de base de votre application et une activité ou un service peut s'appuyer dessus et représente une instance de configuration de votre application ou un état transitif pour celle-ci.

Si vous regardez la source pour android.content.Context, vous voyez que Context est une classe abstraite et les commentaires sur la classe sont les suivants:

Interface vers des informations globales sur un environnement d'application. Il s'agit d'une classe abstraite dont l'implémentation est fournie par le système Android. Il permet l'accès aux application-specificressources et aux classes, ainsi que des appels d'ups pour des application-levelopérations telles que le lancement d'activités, la diffusion et la réception d'intentions, etc. Ressources. Les ressources au niveau de l'application peuvent accéder à des éléments tels que des ressources [getResources()]ou des actifs String[getAssets()] et les ressources au niveau du système sont tout ce à quoi vous accédez avecContext.getSystemService().

En fait, regardez les commentaires sur les méthodes et ils semblent renforcer cette notion:

getSystemService(): Renvoyer le descripteur à un system-levelservice par son nom. La classe de l'objet retourné varie selon le nom demandé. getResources(): Renvoyer une instance de ressources pour le package de votre application. getAssets(): Renvoyer une instance de ressources pour le package de votre application. Il peut être utile de souligner que dans la classe abstraite Context, toutes les méthodes ci-dessus sont abstraites! Une seule instance de getSystemService (Class) a une implémentation et qui appelle une méthode abstraite. Cela signifie que leur implémentation devrait être fournie principalement par les classes d'implémentation, qui comprennent:

ContextWrapper
Application
Activity
Service
IntentService

En regardant la documentation de l'API, la hiérarchie des classes ressemble à ceci:

Le contexte

| - ContextWrapper

| - - Application

| - - ContextThemeWrapper

| - - - - Activité

| - - Un service

| - - - IntentService

Puisque nous savons que Contextlui - même ne fournit aucun aperçu, nous descendons l'arbre et jetons un coup d'œil à ContextWrapperet réalisons qu'il n'y a pas grand-chose non plus. Étant donné que l'application s'étend ContextWrapper, il n'y a pas grand-chose à voir non plus, car il ne remplace pas l'implémentation fournie par ContextWrapper. Cela signifie que l'implémentation de Context est fournie par le système d'exploitation et est masquée par le API. Vous pouvez jeter un œil à l'implémentation concrète de Context en consultant la source de la classe ContextImpl.

Chanaka Weerasinghe
la source
0

Je ne l'ai utilisé que getBaseContextlors du grillage d'un onClick(noob très vert à la fois Java et Android). Je l'utilise lorsque mon clicker est directement dans l'activité et je dois l'utiliser getBaseContextdans un clicker interne anonyme. Je suppose que c'est à peu près le truc avec getBaseContext, c'est peut-être retourner le contexte de l'activité dans laquelle se cache la classe interne.

Tony
la source
1
C'est faux, cela renvoie le contexte de base de l'activité elle-même. Pour obtenir l'activité (celle que vous souhaitez utiliser comme contexte) d'une classe interne anonyme, utilisez quelque chose comme MyActivity.this. L'utilisation du contexte de base tel que vous le décrivez ne causera probablement pas de problèmes mais c'est faux.
nickmartens1980