403 votes

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

Dans divers morceaux de code Android que j'ai vus :

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

Cependant, je n'ai pas trouvé d'explication satisfaisante sur ce qui est préférable et sur les circonstances dans lesquelles il convient d'utiliser l'un ou l'autre.

Des indications sur la documentation à ce sujet et des conseils sur ce qui pourrait se produire si le mauvais choix est fait, seraient très appréciés.

2 votes

Ce lien pourrait vous aider. Passez par este ..

312voto

snctln Points 8687

Je suis d'accord pour dire que la documentation est rare en ce qui concerne les contextes dans Android, mais vous pouvez rassembler quelques faits à partir de diverses sources.

Cet article de blog sur le blog officiel des développeurs Google Android a été rédigé principalement pour aider à résoudre les problèmes de fuites de mémoire, mais fournit également de bonnes informations sur les contextes :

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

En lisant l'article un peu plus loin, vous découvrirez la différence entre les deux et vous saurez quand vous pourrez envisager d'utiliser l'application Contexte ( Activity.getApplicationContext() ) plutôt que d'utiliser le contexte de l'activité this ). En principe, le contexte d'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 pourrait être détruit plusieurs fois, car l'activité est détruite lors des changements d'orientation de l'écran, etc.

Je n'ai pas trouvé d'autres informations sur l'utilisation de getBaseContext() que celle de Dianne Hackborn, l'une des ingénieures de Google travaillant sur le SDK Android :

Ne pas utiliser getBaseContext le contexte que vous avez.

Cela provient d'un post sur le site Groupe de discussion des développeurs Android Vous pouvez également envisager de poser votre question à cet endroit, car une poignée de personnes travaillant sur Android surveillent ce groupe de discussion et répondent aux questions.

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

14 votes

Lorsque j'ai une activité A qui peut démarrer une 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 construire 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 alors l'ancien A fuira. Il semble donc que getBaseContext soit le choix le plus approprié dans la plupart des cas. Ensuite, il n'est pas clair pourquoi Don't use getBaseContext() . Quelqu'un pourrait-il clarifier ce point ?

2 votes

Comment accéder à l'objet contextuel à l'intérieur d'une classe qui n'étend pas Activity ?

1 votes

@Cole, vous pourriez 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.

56voto

ChuongPham Points 970

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

1) . Dans un délai de Activity lui-même, utiliser this pour gonfler les présentations et les menus, enregistrer les menus contextuels, instancier les widgets, démarrer d'autres activités, créer de nouvelles activités. Intent au sein d'un Activity , l'instanciation des préférences, ou d'autres méthodes disponibles dans une Activity .

Gonfler la mise en page :

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

Instancie le widget :

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

Démarrer un Activity :

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

Instancier les préférences :

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

2) . Pour une classe d'application, utilisez getApplicationContext() car ce contexte existe pendant toute la durée de vie de l'application.

Récupère le nom du paquetage 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;
    }
}

Lier une classe pour l'ensemble 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 Listeners et les autres types de classes Android (par exemple ContentObserver), utilisez une substitution de Contexte comme :

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

donde this o context est le contexte d'une classe (Activité, etc.).

Activity la 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 de l'auditeur :

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

ContentObserver la 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é), utiliser le contexte propre au 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);
        }
    }
}

Intégré/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 contexte propre au 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 les toasts, on utilise généralement getApplicationContext() mais, dans la mesure du possible, utiliser le contexte transmis par une activité, un service, etc.

Contexte d'utilisation de l'application :

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

Utiliser le contexte transmis par 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();
    }
}

Enfin, n'utilisez pas getBaseContext() comme le conseillent les développeurs du framework Android.

UPDATE : Ajouter des exemples de Context l'utilisation.

1 votes

Au lieu de mContext, on peut utiliser OuterClass.this ; voir les commentaires dans stackoverflow.com/questions/9605459/

3 votes

+1 pour cette réponse si utile ! Je suis d'accord pour dire que la réponse acceptée est très bien en tant que réponse acceptée, mais cette réponse est très instructive ! 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.

13voto

Mav3656 Points 147

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

Cependant, j'ai rencontré un problème avec ceci, j'ai passé quelques heures à le trouver, et quelques secondes à le résoudre... (en changeant 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 quand vous cliquez sur le spinner dans votre activité, vous aurez une exception non prise, venant de la machine virtuelle dalvik (pas de votre code, c'est pourquoi j'ai passé beaucoup de temps à trouver où était mon erreur...).

Si vous utilisez le baseContext, tout va bien, le menu contextuel s'ouvrira et vous pourrez faire votre choix.

Voici donc ma conclusion : Je suppose (je n'ai pas testé plus avant) que le baseContext est nécessaire lorsque vous utilisez le contextMenu dans votre Activity...

Le test a été effectué 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 ;-)

0 votes

J'ai toujours utilisé "this" pour créer des vues dans une activité. En effet, 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, comme indiqué dans le blog du développeur, est que lorsqu'une ImageView est détruite, le dessin/bitmap utilisé peut s'accrocher à ce contexte. Néanmoins, c'est ce que je fais pour l'instant. En ce qui concerne le code ailleurs dans l'application (classes normales), j'utilise simplement le contexte de l'application car il n'est pas spécifique à une activité ou à des éléments de l'interface utilisateur.

6voto

samsonsu Points 226

Tout d'abord, je suis d'accord sur le fait que nous devrions utiliser appcontext chaque fois que possible. puis "this" dans activity. je n'ai jamais eu besoin de basecontext.

Dans mes tests, ils peuvent être interchangés dans la plupart des cas. Dans la plupart des cas, la raison pour laquelle vous voulez vous emparer d'un contexte est d'accéder à des fichiers, à des préférences, à une 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 vers le même dossier/fichier, donc tout va bien.

C'est ce que j'ai observé. Il y a peut-être des cas où il faut les distinguer.

0 votes

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

3voto

Paul Points 804

Dans certains cas, vous pouvez utiliser le contexte de l'activité plutôt que le contexte de l'application lorsque vous exécutez quelque chose dans un thread. Lorsque le fil d'exécution se termine et que vous devez renvoyer le résultat à l'activité appelante, vous avez besoin de ce contexte avec un gestionnaire.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X