web-dev-qa-db-fra.com

Manière statique d'obtenir le «contexte» dans Android?

Existe-t-il un moyen d’obtenir l’instance actuelle Context à l’intérieur d’une méthode statique?

C'est ce que je recherche car je déteste sauvegarder l'instance 'Context' chaque fois qu'elle change.

911
Andrea Baccega

Faire ceci:

Dans le fichier manifeste Android, déclarez ce qui suit.

<application Android:name="com.xyz.MyApplication">

</application>

Puis écris le cours:

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, appelez partout MyApplication.getAppContext() pour obtenir le contexte de votre application de manière statique.

1234
Rohit Ghatol

La majorité des applications qui recherchent une méthode pratique pour obtenir le contexte de l'application créent leur propre classe, qui s'étend Android.app.Application .

GUIDE

Vous pouvez y parvenir 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 le tag AndroidManifest.xml:

_<application 
    ...
    Android:name="com.example.App" >
    ...
</application>
_

Vous pouvez ensuite récupérer le contexte de l'application dans n'importe quelle méthode statique à l'aide de ce qui suit:

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

WARNING

Avant d’ajouter quelque chose comme ce qui précède à votre projet, réfléchissez à ce que dit la documentation:

Il n’est normalement pas nécessaire de sous-classer 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), vous pouvez attribuer à la fonction de récupération un contexte utilisant en interne Context.getApplicationContext () lors de la première construction du singleton.


RÉFLEXION

Il existe également un autre moyen d'obtenir le contexte d'application en utilisant la réflexion. La réflexion est souvent méprisée dans Android et je pense personnellement que cela ne devrait pas être utilisé en 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 encore une classe cachée ( AppGlobals ) qui permet d'obtenir le contexte de l'application de manière statique. Il obtient le contexte en utilisant ActivityThread. Il n'y a donc aucune différence entre la méthode suivante et celle publiée ci-dessus:

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

Bonne codage!

76
Jared Rummler

En supposant que nous parlions d'obtenir le contexte d'application, je l'ai implémenté comme suggéré par l'extension @Rohit Ghatol. Ce qui s’est passé alors, c’est que rien ne garantit 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 attendre plus longtemps. le traitement du cas nul ne vous aidera pas. J'ai donc compris que je luttais essentiellement contre l'architecture Android, comme indiqué dans le docs

Remarque: Il n'est normalement pas nécessaire de sous-classer 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 (pour enregistrer des récepteurs de diffusion, par exemple), incluez Context.getApplicationContext () en tant qu'argument de contexte lors de l'appel de la méthode getInstance () de votre singleton.

et expliqué par Dianne Hackborn

La seule raison pour laquelle Application existe, c’est parce que pendant le développement avant la version 1.0, un de nos développeurs d’applications me disait constamment qu’il me fallait un objet d’application de niveau supérieur à partir duquel ils pouvaient dériver afin d’avoir un comportement "normal". "Pour eux modèle d'application, et j'ai finalement donné :)

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

Si vous voulez un état global pouvant être partagé entre différentes parties de votre application, utilisez un singleton. [...] Et cela nous amène plus naturellement à la façon dont vous devriez gérer ces choses - en les initialisant à la demande.

donc, ce que j'ai fait était de me débarrasser de l'extension Application et de passer le contexte directement à getInstance () de l'aide unique, tout en sauvegardant 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 ensuite un contexte local à l'aide:

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 doivent tous être découragés et vous devriez préférer passer un contexte local à getInstance () du singleton.


Pour ceux qui sont intéressés, vous pouvez lire une version plus détaillée sur blog fwd

50
Alessio

Non, je ne pense pas qu'il y en ait. Malheureusement, vous êtes en train d'appeler getApplicationContext() depuis Activity ou l'une des autres sous-classes de Context. En outre, this question est un peu liée.

48
Erich Douglass

Voici un moyen non documenté d'obtenir un Application (qui est un contexte) à partir de n'importe où dans le thread d'interface utilisateur. Il repose sur la méthode statique masquée ActivityThread.currentApplication(). Cela 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 pour cette méthode de renvoyer 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.

37
kennytm

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

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

32
gulchrider

Si vous êtes prêt à utiliser RoboGuice , vous pouvez injecter le contexte dans la classe de votre choix. Voici un petit échantillon de la procédure à suivre avec RoboGuice 2.0 (version bêta 4 au moment de la rédaction de cet article).

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) {
        }
    }
}
11
user605331

Je l'ai utilisé à 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 pour lequel j'ai travaillé.

Mais je ne l’utilisais que dans les modifications de structure/base et ne l’essayais pas dans les applications Android.

A warning que vous devez savoir: Lors de l'inscription pour les récepteurs de diffusion avec ce contexte, cela ne fonctionnera pas et vous obtiendrez:

Java.lang.SecurityException: Le package d'appelant donné Android ne fonctionne pas dans le processus ProcessRecord

8
ungalcrys

façon Kotlin:

Manifeste:

<application Android:name="MyApplication">

</application>

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

5
phnmnn

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 un contexte comme

MyApp.mInstance

ou

MyApp.getContext()
4
Khemraj

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();
3
barwnikk

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

3
Sheharyar

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

Mise en œuvre par proxy 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.

3
BlueWizard

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

public static Context getAppContext()
   return MyApplication.context; 
3
Kognos

J'utilise une variante du motif Singleton pour m'aider à résoudre ce problème.

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 activity.onCreate () et ApplicationContextSingleton.setContext( null ); dans onDestroy ();

2
Bamaco

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

La classe centrale $ facade conserve un WeakReference (lien vers le blog génial Java de ce sujet par Ethan Nicholas) dans le contexte actuel de Activity. que vous pouvez récupérer en appelant:

$.act()

WeakReference conserve une référence sans empêcher la récupération de place de récupérer l'objet d'origine, vous ne devriez donc pas avoir de problème de fuites de mémoire.

L’inconvénient est bien sûr que vous courez le risque que $.act() puisse renvoyer null. Je n’ai pas encore rencontré ce scénario, c’est donc peut-être 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 Activity:

$.act(Activity);

En outre, une grande partie de la structure Vapor API utilise ce contexte stocké de manière inhérente, ce qui signifie que vous n’avez peut-être pas besoin de le stocker vous-même si vous décidez d’utiliser le cadre. Consultez le site pour plus d'informations et d'échantillons.

J'espère que ça aide :)

1
Darius

Si, pour une raison quelconque, vous souhaitez un contexte d’application dans n’importe quelle classe, pas seulement celles qui étendent une application/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;
    }
}

puis initialisez-le dans onCreate de votre classe d'application avec

GlobalAppContextSingleton.getInstance().initialize(this);

utilisez-le n'importe où en appelant

GlobalAppContextSingleton.getInstance().getApplicationContext()

Cependant, je ne recommande pas cette approche à tout, sauf au contexte d'application. Comme cela peut causer des fuites de mémoire.

1
Versa

La réponse de Rohit semble correcte. Toutefois, sachez que "l'exécution instantanée" d'AndroidStudio dépend de l'absence d'attributs static Context dans votre code, pour autant que je sache.

1
payne

J'ai donc modifié la réponse acceptée car elle provoque une fuite de mémoire. C'est ce que j'ai proposé ...

AndroidManifest.xml

    <application Android:name="com.xyz.MyApplication">
...

    </application>

MonApplication.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 assigner un contexte à un objet et le renvoyer en tant que contexte (en le transformant en contexte). J'espère que ça vous aidera.

0
DevMike01

dans Kotlin, le fait de placer Context/App Context dans un objet compagnon produit toujours un avertissement Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

ou si vous utilisez quelque chose comme ceci:

    companion object {
        lateinit var instance: MyApp
    }

Il s'agit simplement de tromper la charpie pour ne pas découvrir la fuite de mémoire. L'instance d'application peut toujours produire une fuite de mémoire, car la classe d'applications et son descendant sont un contexte.

Vous pouvez également utiliser l'interface fonctionnelle ou les propriétés fonctionnelles pour vous aider à obtenir le contexte de votre application.

Créez simplement une classe d'objet:

object CoreHelper {
    lateinit var contextGetter: () -> Context
}

ou vous pouvez l'utiliser de manière plus sécurisée en utilisant le type nullable:

object CoreHelper {
    var contextGetter: (() -> Context)? = null
}

et dans votre classe App, ajoutez cette ligne:


class MyApp: Application() {

    override fun onCreate() {
        super.onCreate()
        CoreHelper.contextGetter = {
            this
        }
    }
}

et dans votre manifeste, déclarez le nom de l'application sur . MyApp


    <application
            Android:name=".MyApp"

Lorsque vous voulez obtenir le contexte, appelez simplement:

CoreHelper.contextGetter()

// or if you use the nullable version
CoreHelper.contextGetter?.invoke()

J'espère que ça va aider.

0
Hayi Nukman