Façon statique d'obtenir "contexte" dans Android?
y a-t-il un moyen d'obtenir l'instance actuelle Context
à l'intérieur d'une méthode statique?
c'est ce que je cherche parce que je déteste sauver l'instance 'Context' chaque fois qu'elle change.
20 réponses
Faites ceci:
dans le fichier de manifeste Android, déclarez ce qui suit.
<application android:name="com.xyz.MyApplication">
</application>
alors é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;
}
}
maintenant partout appelez MyApplication.getAppContext()
pour obtenir votre contexte d'application statiquement.
la majorité des applications qui veulent une méthode commode pour obtenir le contexte d'application créer leur propre classe qui étend android.app.Application
.
GUIDE
vous pouvez accomplir ceci en créant d'abord une classe dans votre projet comme le suivant:
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 L'AndroidManifest.xml est mot clé:
<application
...
android:name="com.example.App" >
...
</application>
vous pouvez alors récupérer le contexte de l'application dans n'importe quelle méthode statique en utilisant ce qui suit:
public static void someMethod() {
Context context = App.getContext();
}
avertissement
avant d'ajouter quelque chose comme ce qui précède à votre projet, vous devriez considérer ce que la documentation dit:
il n'est normalement pas nécessaire d'appliquer la sous-classe. Dans la plupart des situation, statique singletons peut fournir la même fonctionnalité dans un plus modulaire façon. Si votre singleton a besoin d'un contexte global (par exemple pour enregistrer récepteurs de radiodiffusion), la fonction de le récupérer peut être donné un Contexte qui utilise le contexte à l'interne.getcontexteapplication() lorsque d'abord construire le singleton.
réflexion
Il y a aussi une autre façon d'obtenir l'application contexte utilisant la réflexion. La réflexion est souvent méprisée sur Android et je pense personnellement que cela ne devrait pas être utilisé dans la production.
pour récupérer le contexte de l'application nous devons invoquer une méthode sur une classe cachée ( ActivityThread ) qui est disponible depuis API 1:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.ActivityThread")
.getMethod("currentApplication").invoke(null, (Object[]) null);
}
il y a encore une classe cachée ( AppGlobals ) qui fournit une façon d'obtenir le contexte de l'application dans un de manière statique. Il obtient le contexte en utilisant ActivityThread
donc il n'y a vraiment aucune 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);
}
bon codage!
Non, Je ne pense pas. Malheureusement, vous êtes coincé appelant getApplicationContext()
de Activity
ou l'une des autres sous-classes de Context
. Aussi, cette question est quelque peu liée.
Voici une non documentée façon d'obtenir une Application (qui est un contexte) à partir de n'importe où dans le thread D'UI. Il s'appuie sur la méthode statique cachée ActivityThread.currentApplication()
. Ça devrait marcher 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 pour cette méthode de retourner null, par exemple lorsque vous appelez la méthode en dehors du thread UI, ou que l'application n'est pas liée au thread.
il est encore mieux utiliser la solution de @RohitGhatol si vous pouvez changer le code d'Application.
en supposant que nous parlons d'obtenir le contexte de L'Application, je l'ai mis en œuvre comme suggéré par @Rohit Ghatol extension de L'Application. Ce qui s'est passé ensuite, c'est qu'il n'y a aucune garantie que le contexte récupéré d'une telle manière sera toujours non-null. Au moment où vous en avez besoin, c'est généralement parce que vous voulez initialiser un helper, ou obtenir une ressource, que vous ne pouvez pas retarder dans le temps; traiter le cas nul ne vous aidera pas. J'ai compris que je me battais contre L'Androïde. architecture, Comme indiqué dans le docs
Note: il n'est normalement pas nécessaire d'appliquer la sous-classe. Dans la plupart des situations, les singletons statiques peuvent fournir la même fonctionnalité d'une manière plus modulaire. Si votre singleton a besoin d'un contexte global (par exemple pour enregistrer des récepteurs de diffusion), incluez le contexte.getApplicationContext() comme argument de contexte lorsque vous invoquez la méthode getInstance () de votre singleton.
et expliqué par Dianne Hackborn
la seule raison pour laquelle Application existe comme quelque chose que vous pouvez dériver est parce que pendant le développement pré-1.0 l'un de nos développeurs d'application me harcelait continuellement sur le besoin d'avoir un objet d'application de haut niveau qu'ils peuvent dériver de sorte qu'ils pourraient avoir un modèle d'application plus" normal " pour eux, et j'ai finalement cédé. Je vais regretter de donner dans 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 mène plus naturellement à la façon dont vous devriez gérer ces choses-les initialiser à la demande.
donc ce que j'ai fait était de me débarrasser de l'extension de L'Application, et passer le contexte directement à la gettinstance () de singleton helper, tout en sauvegardant une référence au contexte d'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 passera alors un contexte local au helper:
Helper.getInstance(myCtx).doSomething();
donc, pour répondre correctement à cette question: il y a des moyens d'accéder au contexte de L'Application de manière statique, mais ils devraient tous être découragés, et vous devriez préférer passer un contexte local au getInstance du singleton().
pour toute personne intéressée, vous pouvez lire une version plus détaillée à FWD blog
cela dépend de ce pour quoi 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 Application
ne fonctionnera pas. Je crois que vous avez besoin du contexte pour l'actuel Activity
...
si vous êtes ouvert à l'utilisation de RoboGuice , vous pouvez avoir le contexte injecté dans n'importe quelle classe que vous voulez. Voici un petit échantillon de la façon de le faire avec RoboGuice 2.0 (bêta 4 au moment de cet écrit)
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 de système et a fonctionné.
mais, je l'ai utilisé seulement dans les modifications de cadre/base et ne l'ai pas essayé dans les applications Android.
Un avertissement que vous devez savoir: Lors de l'inscription pour récepteurs de radiodiffusion avec ce contexte, il ne fonctionnera pas et vous obtiendrez:
de java.lang.SecurityException: android ne s'exécute pas dans le processus 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 ce qui suit:
MainActivity.this.getApplicationContext();
activité principale.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();
}
}
mise en œuvre du contexte qui délègue simplement tous ses appels à un autre contexte. Peut être sous-classé pour modifier le comportement sans changer le contexte d'origine.
j'utilise une variation du design de Singleton pour m'aider avec ça.
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 ensuite ApplicationContextSingleton.setContext( this );
dans mon activité .onCreate () et ApplicationContextSingleton.setContext( null );
in onDestroy () ;
je viens de sortir un framework d'inspiration jQuery pour Android appelé vapor API qui vise à simplifier le développement d'applications.
the central $
facade class maintains a WeakReference
(lien vers le blog Java impressionnant post à ce sujet par Ethan Nicholas) à l'actuel Activity
contexte que vous pouvez récupérer en appelant:
$.act()
Un WeakReference
maintient une référence sans empêcher la collecte des ordures de récupérer l'objet original, de sorte que vous ne devriez pas avoir de problème avec les fuites de mémoire.
l'inconvénient est que vous courez le risque que $.act()
puisse retourner nul. 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 votre classe Activity
:
$.act(Activity);
aussi, une grande partie du Vapor API framework utilise ce contexte stocké par nature, ce qui pourrait 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 des échantillons.
j'espère que cela aide :)
si vous voulez pour une raison quelconque le contexte D'Application dans n'importe quelle classe, pas seulement ceux qui étendent l'application/l'activité, peut-être pour certaines classes d'usine ou d'aide. Vous pouvez ajouter 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;
}
}
alors initialisez - le dans l'onCreate de votre classe d'application avec
GlobalAppContextSingleton.getInstance().initialize(this);
utilisez-le n'importe où en appelant
GlobalAppContextSingleton.getInstance().getApplicationContext()
Je ne recommande cette approche que dans le contexte de l'application cependant. Car il peut causer des fuites de mémoire.
donc j'ai modifié la réponse acceptée parce que ça cause une fuite de mémoire, c'est ce que j'ai trouvé...
AndroidManifest.xml
<application android:name="com.xyz.MyApplication">
...
</application>
ma demande.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 est, affecter un contexte à un objet et retourne l'objet de contexte(moulage). Espérons qu'il l'aide.
Si vous ne voulez pas modifier le fichier de manifeste, vous pouvez sauvegarder manuellement le contexte d'une variable statique dans 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 mettez juste le contexte quand votre activité (ou vos activités) commence:
// MainActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set Context
App.setContext(getApplicationContext());
// Other stuff
}
Note: comme toutes les autres réponses, il s'agit d'une fuite de mémoire potentielle.
Kotlin façon :
manifeste:
<application android:name="MyApplication">
</application>
ma demande.kt
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApplication
private set
}
}
vous pouvez alors 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;
}
}
j'obtiens 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)
.
mais si j'utilise le code suivant:
public class App extends Application {
private static List<Context> context = new ArrayList<>();
@Override
public void onCreate() {
super.onCreate();
context.add(0, getApplicationContext());
}
public static Context getAppContext() {
return context.get(0);
}
}
il fonctionne très bien sans aucune erreur.
la réponse de Rohit semble correcte. Cependant, sachez que la "course instantanée" D'AndroidStudio dépend de l'absence des attributs static Context
dans votre code, autant que je sache.
Dans Kotlin
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
et obtenir le contexte comme
App.mInstance
ou
MyApp.getContext()