web-dev-qa-db-fra.com

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

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

 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 sur ce qui est préférable et dans quelles circonstances il convient de l'utiliser.

Des indications sur la documentation à ce sujet et sur ce qui pourrait casser si le mauvais choix était choisi seraient très appréciées.

384
Alnitak

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

Cet article de blog sur le blog officiel des développeurs de Google Android a été écrit principalement pour aider à remédier aux fuites de mémoire, mais fournit également des informations utiles sur les contextes:

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

En lisant cet article un peu plus loin, vous parlez de la différence entre les deux et de la possibilité d'utiliser le contexte de l'application (Activity.getApplicationContext()) plutôt que le contexte de l'activité 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, le contexte de l'activité étant associé à l'activité et pouvant éventuellement être détruit à plusieurs reprises si l'activité est détruite lors des changements d'orientation de l'écran et tel.

Je ne pouvais vraiment pas savoir quand utiliser getBaseContext () en dehors d'un message de Dianne Hackborn, l'un des ingénieurs de Google travaillant sur le Android SDK:

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

C'était à partir d'un article sur forum de discussion des développeurs Android , vous pouvez également envisager de poser votre question ici, car une poignée de personnes travaillant sur Android surveillent réellement ce groupe de discussion et répondez aux questions.

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

302
snctln

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

1). Au sein d'une Activity, utilisez this pour gonfler les présentations et les menus, pour enregistrer les menus contextuels, pour instancier des widgets, pour démarrer d'autres activités, pour créer un nouveau Intent dans un Activity, des préférences d'instanciation ou d'autres méthodes disponibles dans un Activity.

Gonfler la mise en page:

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

menu Gonfler:

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

Instantiate widget:

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

Démarrer une Activity:

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

Instantiate préférences:

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

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

Récupère 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;
    }
}

Lier une classe d'application:

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

). Pour les auditeurs et les autres types de classes Android (par exemple, ContentObserver), utilisez une substitution de contexte telle que:

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

this ou context est le contexte d'une classe (Activity, 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 de contexte d'auditeur:

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 (récepteur inclus/intégré), utilisez le propre contexte du destinataire.

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

En ligne/Embarqué 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 les toasts, utilisez généralement getApplicationContext(), mais dans la mesure du possible, utilisez le contexte transmis par une activité, un service, etc.

tilisez le contexte de l'application:

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

tiliser le contexte passé d'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 conseillé par les développeurs de framework Android.

PDATE: Ajoutez des exemples d'utilisation de Context.

51
ChuongPham

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 à le trouver et quelques secondes à le résoudre ... (changer d'un mot ...)

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

Donc, voici 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 instanciez votre linearLayout avec applicationContext, puis lorsque vous cliquez sur la visière dans votre activité, vous aurez une exception non capturée, provenant de la machine virtuelle Dalvik (et non de votre code, c’est pourquoi j’ai passé beaucoup de temps. de temps pour trouver où était mon erreur ...).

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

Donc voici ma conclusion: je suppose (je ne l'ai pas encore testé) que la baseContext est requise pour gérer contextMenu dans votre activité ...

Le test a été effectué avec l'API 8 et avec un HTC Desire, Android 2.3.3.

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

12
Mav3656

Premièrement, je conviens que nous devrions utiliser appcontext chaque fois que possible. puis "ceci" en activité. Je n'ai jamais eu besoin de Basecontext.

Dans mes tests, dans la plupart des cas, ils peuvent être interchangés. Dans la plupart des cas, si vous souhaitez maîtriser un contexte, vous souhaitez accéder à des fichiers, préférences, bases 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 /). Peu importe le contexte que vous utilisez, ils seront mappés sur le même dossier/fichier, de sorte que tout va bien.

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

6
samsonsu

Dans certains cas, vous pouvez utiliser le contexte d'activité par rapport au contexte d'application lors de l'exécution de quelque chose dans un thread. Lorsque le thread a terminé son 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, ...);
3
Paul

En mots simples

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

getActivity() ou this rendra votre application consciente de l'écran actuel qui est visible ainsi que des détails au niveau de l'application fournis par application context. Donc, tout ce que vous voulez savoir sur l’écran actuel, tel que WindowActionBarFragementmanger et sont donc disponibles avec ce contexte. Fondamentalement et Activity étendre Context. Ce contexte restera actif jusqu'à ce que le composant actuel (activité) soit actif

2
arjun

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

_getContext()
getBaseContext()
getApplicationContext()
getActionBar().getThemedContext() //new
_

Qu'est-ce qu'un contexte? Personnellement, j'aime bien considérer le contexte comme l'état de votre application à un moment donné. Le contexte de l'application représente une configuration globale ou de base de votre application. Une activité ou un service peut la développer et représente une instance de configuration de votre application ou un état transitif de celle-ci.

Si vous regardez le code source pour Android.content.Context, vous voyez que Context est une classe abstraite et que les commentaires sur cette classe sont les suivants:

Interface avec 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 ressources et classes _application-specific_, ainsi qu’aux appels supplémentaires pour les opérations _application-level_ telles que le lancement d’activités, la diffusion et la réception d’intentions, etc. Ce que j’en déduis, c’est que le contexte fournit un mise en œuvre pour accéder au niveau de l'application ainsi qu'aux ressources du système. Les ressources au niveau de l’application peuvent accéder à des éléments tels que les ressources String [getResources()] ou aux ressources [getAssets()], et les ressources système sont tout ce à quoi vous accédez avec Context.getSystemService()..

En fait, jetez un coup d’œil aux commentaires sur les méthodes et elles semblent renforcer cette notion:

getSystemService(): renvoie le descripteur à un service _system-level_ par son nom. La classe de l'objet renvoyé varie en fonction du nom demandé. getResources(): renvoie une instance de ressources pour le package de votre application. getAssets(): renvoie une instance de ressources pour le package de votre application. Il peut être intéressant 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 invoque une méthode abstraite. Cela signifie que leur implémentation devrait être assurée principalement par les classes d'implémentation, notamment:

_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 Context ne fournit aucune idée, nous descendons l’arbre pour jeter un coup d’œil à la ContextWrapper et nous réalisons qu’il n’y en a pas beaucoup. Etant donné que l’application étend ContextWrapper, il n’ya pas grand-chose à regarder là-bas non plus car elle 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 cachée de la variable API. Vous pouvez examiner l'implémentation concrète de Context en consultant le source de la classe ContextImpl.

0
ORBIT

Je n'ai utilisé que ceci et getBaseContext lors de la cuisson d'un onClick (noob très vert à Java et à Android). J'utilise ceci lorsque mon clicker est directement dans l'activité et que je dois utiliser getBaseContext dans un clicker interne anonyme. J'imagine que c'est assez le truc avec getBaseContext, c'est peut-être de retourner le contexte de l'activité dans laquelle se cache la classe intérieure.

0
Tony