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

StackOverflow https://stackoverflow.com/questions/1026973

  •  06-07-2019
  •  | 
  •  

Question

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 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.

Était-ce utile?

La solution

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

Ce billet de blog sur Google Android officiel Le blog des développeurs 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 Android classique, vous   ont généralement deux types de contexte,   Activité et 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 d'utiliser le contexte d'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 SDK Android:

  

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

Cela provenait d'un message publié sur groupe de discussion android-developers , vous pouvez également envisager de poser votre question ici, car une poignée de personnes travaillant sur Android surveillent ce groupe de discussion et répondent à des questions.

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

Autres conseils

Voici ce que j'ai trouvé concernant l'utilisation du contexte :

1). Dans une activité elle-même, utilisez this pour gonfler les présentations et les menus, enregistrer des menus contextuels, instancier des widgets, démarrer d'autres activités. , créez un nouveau Intention dans une activité , des préférences d'instanciation ou d'autres méthodes disponibles dans une activité .

Mise en page gonflée:

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

Menu contextuel Enregistrer:

this.registerForContextMenu(myView);

Instancier le widget:

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

Démarrer une activité :

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

Instancier les 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é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;
    }
}

Lier une classe au niveau 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 auditeurs et autres types de classes Android (par exemple, ContentObserver), utilisez une substitution de contexte telle que:

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

ce ou contexte est le contexte d'une classe (Activité, etc.).

Activité de remplacement de code :

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

Substitution de contexte ContentObserver :

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 incorporé / incorporé), utilisez le propre contexte du destinataire.

ExternalReceiver :

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

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

.

Utiliser le contexte de l'application:

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

Contexte d'utilisation 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.

UPDATE: ajouter des exemples d'utilisation de contexte .

J'ai lu ce fil il y a quelques jours en me posant la même question. Ma décision après avoir lu cela é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.

Il y a 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 instanciez votre linearLayout avec l’applicationContext, lorsque vous cliquez sur la visionneuse dans votre activité, vous aurez une exception non capturée, provenant de la machine virtuelle Dalvik (pas de votre code, c’est pourquoi passé beaucoup de temps à trouver où était mon erreur ...).

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

Voici donc ma conclusion: je suppose (je ne l'ai pas encore testé) que le contexte de base est requis pour traiter le contextMenu dans votre activité ...

Le test a été effectué avec l’API 8 et sur 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. Code heureux ;-)

Tout d'abord, je conviens que nous devrions utiliser appcontext chaque fois que cela est possible. alors " this " 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 en tant que 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 / les mêmes fichiers, 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.

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 fil. 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, ...);

En mots simples

getApplicationContext () , comme le suggère le nom de la méthode, permettra à votre application de connaître les détails généraux de l'application auxquels vous pouvez accéder depuis n'importe où dans l'application. Vous pouvez donc utiliser ceci dans la liaison de service, l'enregistrement de diffusion, etc. contexte d'application restera actif jusqu'à la fermeture de l'application.

getActivity () ou this permettra à votre application de connaître l'écran actuel qui est visible ainsi que les détails de son niveau fournis par le contexte d'application . . Ainsi, tout ce que vous voulez savoir sur l'écran actuel, tel que Fenêtre ActionBar Fragementmanger , est disponible dans ce contexte. En gros et Activité étendre Contexte . Ce contexte restera actif jusqu'à ce que le composant (activité) actuel soit actif

Je n'ai utilisé cela que getBaseContext lors du grillage à partir d'un onClick (noob très vert pour 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. Je suppose que c’est à peu près tout l’astuce de getBaseContext , c’est peut-être de renvoyer le contexte de l’activité dans laquelle se cache la classe interne.

  

La confusion vient du fait qu’il existe de nombreuses façons de   accès Contexte, avec (à la surface) pas de différences perceptibles.   Vous trouverez ci-dessous quatre des moyens les plus courants auxquels vous pouvez avoir accès.   Contexte dans une activité.

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

Qu'est-ce qu'un contexte? Personnellement, j'aime bien considérer Context 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 d'android.content.Context, vous voyez que le contexte est une classe abstraite et que les commentaires sur la 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 d'accéder aux ressources et aux classes spécifiques à l'application , ainsi qu'aux appels vers le haut pour les opérations au niveau de l'application telles que les activités de lancement, les intentions de diffusion et de réception, etc. Ce que j'en retiens, c'est que Context fournit une implémentation commune 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 les actifs [getAssets ()] et les ressources au niveau du système sont tout ce à quoi vous accédez avec Context.getSystemService ().

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

getSystemService () : Renvoyez le descripteur à un service de niveau système par son nom. La classe de l'objet renvoyé varie en fonction du nom demandé. getResources () : Renvoyez 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

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

Contexte

| & # 8202; & # 8212; & # 8202; ContextWrapper

| & # 8212; & # 8202; & # 8212; & # 8202; Application

| & # 8202; & # 8212; & # 8202; & # 8212; ContextThemeWrapper

| & # 8212; & # 8202; & # 8212; & # 8202; & # 8212; & # 8202; & # 8212; & # 8202; Activité

| & # 8202; & # 8212; & # 8202; & # 8212; Service

| & # 8212; & # 8202; & # 8212; & # 8202; & # 8212; IntentService

Puisque nous savons que Context ne fournit aucun aperçu, nous descendons dans l'arborescence pour jeter un coup d'œil au ContextWrapper et nous réalisons qu'il n'y en a pas beaucoup. non plus. Etant donné que Application étend ContextWrapper , il n’ya pas grand-chose à y regarder 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 cachée à API . Vous pouvez consulter l'implémentation concrète de Context en consultant le source de la classe ContextImpl.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top