Un moyen statique d’obtenir le contexte dans Android?

Existe-t-il un moyen d’obtenir l’instance de Context actuelle dans une méthode statique?

Je cherche cette façon parce que je déteste enregistrer l’instance ‘Context’ chaque fois qu’elle change.

Faites ceci:

Dans le fichier manifeste Android, déclarez les éléments suivants.

   

Ensuite, écrivez la classe:

 public class MyApplication extends Application { private static Context context; public void onCreate() { super.onCreate(); MyApplication.context = getApplicationContext(); } public static Context getAppContext() { return MyApplication.context; } } 

Appelez maintenant partout MyApplication.getAppContext() pour obtenir votre contexte d’application de manière statique.

La majorité des applications qui souhaitent une méthode pratique pour obtenir le contexte de l’application créent leur propre classe qui étend android.app.Application .

GUIDER

Vous pouvez accomplir cela en créant d’abord une classe dans votre projet comme suit:

 import android.app.Application; import android.content.Context; public class App extends Application { private static Application sApplication; public static Application getApplication() { return sApplication; } public static Context getContext() { return getApplication().getApplicationContext(); } @Override public void onCreate() { super.onCreate(); sApplication = this; } } 

Ensuite, dans votre AndroidManifest, vous devez spécifier le nom de votre classe dans la balise AndroidManifest.xml:

  ...  

Vous pouvez ensuite récupérer le contexte d’application dans n’importe quelle méthode statique à l’aide des éléments suivants:

 public static void someMethod() { Context context = App.getContext(); } 

ATTENTION

Avant d’append quelque chose comme ci-dessus à votre projet, vous devez considérer ce que dit la documentation:

Il n’y a normalement pas besoin de sous-classe Application. Dans la plupart des cas, les singletons statiques peuvent fournir les mêmes fonctionnalités de manière plus modulaire. Si votre singleton a besoin d’un contexte global (par exemple pour enregistrer des récepteurs de diffusion), la fonction permettant de le récupérer peut recevoir un contexte qui utilise en interne Context.getApplicationContext () lors de la première construction du singleton.


RÉFLEXION

Il existe également un autre moyen d’obtenir le contexte de l’application en utilisant la reflection. La reflection est souvent méprisée dans Android et je pense personnellement que cela ne devrait pas être utilisé dans la production.

Pour récupérer le contexte d’application, nous devons appeler une méthode sur une classe cachée ( ActivityThread ) disponible depuis l’API 1:

 public static Application getApplicationUsingReflection() throws Exception { return (Application) Class.forName("android.app.ActivityThread") .getMethod("currentApplication").invoke(null, (Object[]) null); } 

Il existe une autre classe cachée ( AppGlobals ) qui permet d’obtenir le contexte de l’application de manière statique. Il obtient le contexte en utilisant ActivityThread donc il n’y a pas vraiment de différence entre la méthode suivante et celle affichée ci-dessus:

 public static Application getApplicationUsingReflection() throws Exception { return (Application) Class.forName("android.app.AppGlobals") .getMethod("getInitialApplication").invoke(null, (Object[]) null); } 

Heureux codage!

Non, je ne pense pas qu’il y en a. Malheureusement, vous êtes bloqué en appelant getApplicationContext() partir d’ Activity ou de l’une des autres sous-classes de Context . En outre, cette question est un peu liée.

Voici un moyen non documenté d’obtenir une application (qui est un contexte) à partir de n’importe où dans le thread d’interface utilisateur. Il s’appuie sur la méthode statique masquée ActivityThread.currentApplication() . Il devrait fonctionner au moins sur Android 4.x.

 try { final Class activityThreadClass = Class.forName("android.app.ActivityThread"); final Method method = activityThreadClass.getMethod("currentApplication"); return (Application) method.invoke(null, (Object[]) null); } catch (final ClassNotFoundException e) { // handle exception } catch (final NoSuchMethodException e) { // handle exception } catch (final IllegalArgumentException e) { // handle exception } catch (final IllegalAccessException e) { // handle exception } catch (final InvocationTargetException e) { // handle exception } 

Notez qu’il est possible que cette méthode renvoie null, par exemple lorsque vous appelez la méthode en dehors du thread d’interface utilisateur ou que l’application n’est pas liée au thread.

Il est toujours préférable d’utiliser la solution de @RohitGhatol si vous pouvez modifier le code de l’application.

En supposant que nous parlions d’obtenir le contexte d’application, je l’ai implémenté comme suggéré par @Rohit Ghatol Extension Application. Qu’est-ce qui s’est passé alors, c’est qu’il n’y a aucune garantie que le contexte récupéré de cette manière sera toujours non nul. Au moment où vous en avez besoin, c’est généralement parce que vous souhaitez initialiser un assistant ou obtenir une ressource que vous ne pouvez pas retarder; le traitement de l’affaire null ne vous aidera pas. Donc, j’ai compris que je me battais essentiellement contre l’architecture Android, comme indiqué dans les docs

Note: Normalement, il n’est pas nécessaire de sous-classe Application. Dans la plupart des situations, les singletons statiques peuvent fournir les mêmes fonctionnalités de manière plus modulaire. Si votre singleton a besoin d’un contexte global (par exemple pour enregistrer des récepteurs de diffusion), incluez Context.getApplicationContext () en tant qu’argument de contexte lorsque vous appelez la méthode getInstance () de votre singleton.

et expliqué par Dianne Hackborn

La seule raison pour laquelle Application existe en tant que quelque chose dont vous pouvez dériver est que pendant le développement antérieur à la version 1.0, l’un de nos développeurs d’applications continuait à me demander s’il était nécessaire d’avoir un object applicatif de niveau supérieur. “Pour eux, le modèle d’application, et j’ai finalement cédé. Je regretterai toujours de céder à celui-là. 🙂

Elle suggère également la solution à ce problème:

Si ce que vous voulez est un état global qui peut être partagé entre différentes parties de votre application, utilisez un singleton. […] Et cela conduit plus naturellement à la façon dont vous devriez gérer ces choses – en les initialisant à la demande.

J’ai donc éliminé l’application et transmis le contexte directement à getInstance () de l’assistant singleton, tout en enregistrant une référence au contexte de l’application dans le constructeur privé:

 private static MyHelper instance; private final Context mContext; private MyHelper(@NonNull Context context) { mContext = context.getApplicationContext(); } public static MyHelper getInstance(@NonNull Context context) { synchronized(MyHelper.class) { if (instance == null) { instance = new MyHelper(context); } return instance; } } 

l’appelant transmettra alors un contexte local à l’assistant:

 Helper.getInstance(myCtx).doSomething(); 

Donc, pour répondre correctement à cette question: il existe des moyens d’accéder au contexte d’application de manière statique, mais ils devraient tous être découragés, et vous devriez préférer passer un contexte local à getInstance () du singleton.


Pour toute personne intéressée, vous pouvez lire une version plus détaillée sur le blog de fwd

Cela dépend de ce que vous utilisez le contexte. Je peux penser à au moins un inconvénient de cette méthode:

Si vous essayez de créer un AlertDialog avec AlertDialog.Builder , le contexte d’ Application ne fonctionnera pas. Je crois que vous avez besoin du contexte pour l’ Activity

Si vous êtes ouvert à l’utilisation de RoboGuice , vous pouvez injecter le contexte dans la classe de votre choix . Voici un petit exemple de comment faire avec RoboGuice 2.0 (beta 4 au moment de la rédaction de ce document)

 import android.content.Context; import android.os.Build; import roboguice.inject.ContextSingleton; import javax.inject.Inject; @ContextSingleton public class DataManager { @Inject public DataManager(Context context) { Properties properties = new Properties(); properties.load(context.getResources().getAssets().open("data.properties")); } catch (IOException e) { } } } 

J’ai utilisé ceci à un moment donné:

 ActivityThread at = ActivityThread.systemMain(); Context context = at.getSystemContext(); 

C’est un contexte valide que j’ai utilisé pour obtenir des services système et fonctionné.

Mais je ne l’ai utilisé que dans des modifications de framework / base et je ne l’ai pas essayé dans les applications Android.

Un avertissement que vous devez savoir: lors de l’enregistrement pour les récepteurs de diffusion avec ce contexte, cela ne fonctionnera pas et vous obtiendrez:

java.lang.SecurityException: le package d’appelant donné par android n’est pas en cours d’exécution ProcessRecord

Je pense que vous avez besoin d’un corps pour la méthode getAppContext() :

 public static Context getAppContext() return MyApplication.context; 

Vous pouvez utiliser les éléments suivants:

 MainActivity.this.getApplicationContext(); 

MainActivity.java:

 ... public class MainActivity ... { static MainActivity ma; ... public void onCreate(Bundle b) { super... ma=this; ... 

Toute autre classe:

 public ... public ANY_METHOD... { Context c = MainActivity.ma.getApplicationContext(); 

Selon cette source, vous pouvez obtenir votre propre contexte en étendant ContextWrapper

 public class SomeClass extends ContextWrapper { public SomeClass(Context base) { super(base); } public void someMethod() { // notice how I can use "this" for Context // this works because this class has it's own Context just like an Activity or Service startActivity(this, SomeRealActivity.class); //would require context too File cacheDir = getCacheDir(); } } 

JavaDoc pour ContextWrapper

Implémenter la mise en œuvre du contexte en déléguant simplement tous ses appels à un autre contexte. Peut être sous-classé pour modifier le comportement sans changer le contexte d’origine.

J’utilise une variante du modèle de conception Singleton pour m’aider avec ceci.

 import android.app.Activity; import android.content.Context; public class ApplicationContextSingleton { private static Activity gContext; public static void setContext( Activity activity) { gContext = activity; } public static Activity getActivity() { return gContext; } public static Context getContext() { return gContext; } } 

J’appelle alors ApplicationContextSingleton.setContext( this ); dans mes activity.onCreate () et ApplicationContextSingleton.setContext( null ); dans onDestroy () ;

Je viens de publier un framework inspiré de jQuery pour Android appelé Vapor API qui vise à simplifier le développement d’applications.

La classe central $ facade conserve un WeakReference (lien vers un blog Java génial à ce propos d’Ethan Nicholas) dans le contexte d’ Activity actuel que vous pouvez récupérer en appelant:

 $.act() 

Un WeakReference conserve une référence sans empêcher la récupération de place de récupérer l’object d’origine. Vous ne devriez donc pas avoir de problème de fuite de mémoire.

L’inconvénient, c’est que vous courez le risque que $.act() puisse renvoyer null. Je ne suis pas encore tombé sur ce scénario, donc c’est peut-être juste un risque minime, qui mérite d’être mentionné.

Vous pouvez également définir le contexte manuellement si vous n’utilisez pas VaporActivity comme classe d’ Activity :

 $.act(Activity); 

En outre, une grande partie de l’ infrastructure de l’API Vapour utilise ce contexte stocké de manière inhérente, ce qui peut signifier que vous n’avez pas besoin de le stocker vous-même si vous décidez d’utiliser le framework. Consultez le site pour plus d’informations et d’échantillons.

J’espère que ça aide 🙂

Si, pour une raison quelconque, vous souhaitez un contexte d’application dans n’importe quelle classe, pas seulement celles qui étendent l’application / l’activité, peut-être pour certaines classes d’usine ou auxiliaires. Vous pouvez append le singleton suivant à votre application.

 public class GlobalAppContextSingleton { private static GlobalAppContextSingleton mInstance; private Context context; public static GlobalAppContextSingleton getInstance() { if (mInstance == null) mInstance = getSync(); return mInstance; } private static synchronized GlobalAppContextSingleton getSync() { if (mInstance == null) mInstance = new GlobalAppContextSingleton(); return mInstance; } public void initialize(Context context) { this.context = context; } public Context getApplicationContext() { return context; } } 

puis l’initialiser dans onCreate de votre classe d’application avec

 GlobalAppContextSingleton.getInstance().initialize(this); 

l’utiliser n’importe où en appelant

 GlobalAppContextSingleton.getInstance().getApplicationContext() 

Je ne recommande toutefois cette approche que dans le contexte de l’application. Comme cela peut provoquer des memory leaks.

J’ai donc modifié la réponse acceptée car elle provoquait une fuite de mémoire, c’est ce que j’ai imaginé …

AndroidManifest.xml

   ...  

MyApplication.java

 public class MyBakingAppContext extends Application { private static Object mContext; public void onCreate() { super.onCreate(); mContext = getApplicationContext(); } public static Context getAppContext() { return (Context)mContext; } } 

Ce que j’ai fait, c’est d’assigner un contexte à un object et de renvoyer l’object en tant que contexte (en le plaçant dans un contexte). J’espère que ça aide.

Si vous ne souhaitez pas modifier le fichier manifeste, vous pouvez stocker manuellement le contexte dans une variable statique de votre activité initiale:

 public class App { private static Context context; public static void setContext(Context cntxt) { context = cntxt; } public static Context getContext() { return context; } } 

Et juste définir le contexte quand votre activité (ou activité) commence:

 // MainActivity @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Set Context App.setContext(getApplicationContext()); // Other stuff } 

Remarque: comme toutes les autres réponses, il s’agit d’une fuite de mémoire potentielle.

Façon Kotlin :

Manifeste:

   

MyApplication.kt

 class MyApplication: Application() { override fun onCreate() { super.onCreate() instance = this } companion object { lateinit var instance: MyApplication private set } } 

Vous pouvez ensuite accéder à la propriété via MyApplication.instance

J’ai testé toutes les suggestions décrites ici et je peux dire:

si j’utilise le code suivant:

 public class App extends Application { private static Context context; public void onCreate() { super.onCreate(); context = getApplicationContext(); } public static Context getAppContext() { return context; } } 

Je reçois dans le Studio 3 un message d’erreur:

Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run) Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run) .

Mais si j’utilise le code suivant:

 public class App extends Application { private static List context = new ArrayList<>(); @Override public void onCreate() { super.onCreate(); context.add(0, getApplicationContext()); } public static Context getAppContext() { return context.get(0); } } 

ça marche bien sans aucune erreur.

La réponse de Rohit semble correcte. Cependant, sachez que “Instant Run” d’AndroidStudio dépend, pour autant que je sache, de l’absence static Context atsortingbuts de static Context dans votre code.