Question Utilisation du contexte d'application partout?


Dans une application Android, y a-t-il quelque chose qui ne va pas dans l’approche suivante:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

et passez-le partout (par exemple SQLiteOpenHelper) où le contexte est requis (et ne fuit pas bien sûr)?


427
2018-06-12 14:54


origine


Réponses:


Il y a quelques problèmes potentiels avec cette approche, bien que dans beaucoup de circonstances (comme votre exemple) cela fonctionnera bien.

En particulier, vous devez faire attention lorsque vous traitez de tout ce qui concerne le GUI cela nécessite un Context. Par exemple, si vous transmettez l'application Contexte dans le LayoutInflater vous obtiendrez une exception. De manière générale, votre approche est excellente: il est conseillé d’utiliser un Activity's  Context dans ce Activity, et le Application Context lors de la transmission d'un contexte au-delà de la portée d'un Activity à éviter les fuites de mémoire.

Aussi, en tant que alternative à votre modèle, vous pouvez utiliser le raccourci d'appel getApplicationContext() sur un Context objet (comme une activité) pour obtenir le contexte d'application.


379
2018-06-12 16:00



Dans mon expérience, cette approche ne devrait pas être nécessaire. Si vous avez besoin du contexte pour quoi que ce soit, vous pouvez généralement l'obtenir via un appel à View.getContext () et en utilisant le contexte obtenu là, vous pouvez appeler Context.getApplicationContext () pour obtenir le contexte de l'application. Si vous essayez d’obtenir le contexte d’application à partir d’une activité, vous pouvez toujours appeler Activity.getApplication () qui devrait pouvoir être passé comme le contexte nécessaire pour un appel à SQLiteOpenHelper ()

Dans l'ensemble, il ne semble pas y avoir de problème avec votre approche pour cette situation, mais lorsque vous traitez avec Context, assurez-vous que vous ne fuyez pas la mémoire, comme décrit sur le site officiel. Blog Google Android Developers


27
2018-06-12 15:57



Certaines personnes ont demandé: Comment le singleton peut-il retourner un pointeur nul? Je réponds à cette question. (Je ne peux pas répondre dans un commentaire car j'ai besoin de poster du code.)

Il peut retourner null entre deux événements: (1) la classe est chargée, et (2) l'objet de cette classe est créé. Voici un exemple:

class X {
    static X xinstance;
    static Y yinstance = Y.yinstance;
    X() {xinstance=this;}
}
class Y {
    static X xinstance = X.xinstance;
    static Y yinstance;
    Y() {yinstance=this;}
}

public class A {
    public static void main(String[] p) {
    X x = new X();
    Y y = new Y();
    System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
    System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
    }
}

Lançons le code:

$ javac A.java 
$ java A
x:X@a63599 y:Y@9036e
x:null y:null

La deuxième ligne montre que Y.xinstance et X.yinstance sont nul; ils sont nuls parce que les variables X.xinstance ans Y.yinstance ont été lus quand ils étaient nuls.

Cela peut-il être réparé? Oui,

class X {
    static Y y = Y.getInstance();
    static X theinstance;
    static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
    static X x = X.getInstance();
    static Y theinstance;
    static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}

public class A {
    public static void main(String[] p) {
    System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
    System.out.println("x:"+Y.x+" y:"+X.y);
    }
}

et ce code ne montre aucune anomalie:

$ javac A.java 
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e

MAIS ce n'est pas une option pour Android Application objet: le programmeur ne contrôle pas l'heure à laquelle il est créé.

Encore une fois: la différence entre le premier exemple et le second est que le second exemple crée une instance si le pointeur statique est nul. Mais un programmeur ne peut pas créer la Application Android objet avant que le système décide de le faire.


11
2017-12-09 06:17



Vous essayez de créer un wrapper pour obtenir le contexte d'application et il est possible qu'il revienne "null"pointeur.

Selon ma compréhension, je suppose que sa meilleure approche pour appeler l'un des 2 Context.getApplicationContext()  ou Activity.getApplication().


9
2018-06-21 06:55



Classe d'application:

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {

    private static Context mContext;

    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static Context getAppContext() {
        return mContext;
    }

}

Déclarez l'application dans AndroidManifest:

<application android:name=".MyApplication"
    ...
/>

Usage:

MyApplication.getAppContext()

9
2017-08-13 10:37



C'est une bonne approche. Je l'utilise moi aussi. Je suggérerais seulement de passer outre onCreate pour définir le singleton au lieu d'utiliser un constructeur.

Et puisque vous avez mentionné SQLiteOpenHelper: Dans onCreate () vous pouvez également ouvrir la base de données.

Personnellement, je pense que la documentation a eu tort de dire que Il n'y a normalement pas besoin de sous-classe Application. Je pense que le contraire est vrai: vous devriez toujours sous-classe Application.


4
2017-08-07 12:46



J'utiliserais Application Context pour obtenir un service système dans le constructeur. Cela facilite les tests et les avantages de la composition

public class MyActivity extends Activity {

    private final NotificationManager notificationManager;

    public MyActivity() {
       this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
    }

    public MyActivity(NotificationManager notificationManager) {
       this.notificationManager = notificationManager;
    }

    // onCreate etc

}

La classe de test utiliserait alors le constructeur surchargé.

Android utiliserait le constructeur par défaut.


3
2018-06-27 08:45



Je l'aime bien, mais je suggère plutôt un singleton:

package com.mobidrone;

import android.app.Application;
import android.content.Context;

public class ApplicationContext extends Application
{
    private static ApplicationContext instance = null;

    private ApplicationContext()
    {
        instance = this;
    }

    public static Context getInstance()
    {
        if (null == instance)
        {
            instance = new ApplicationContext();
        }

        return instance;
    }
}

0
2018-02-23 22:49



J'utilise la même approche, je suggère d'écrire un peu mieux le singleton:

public static MyApp getInstance() {

    if (instance == null) {
        synchronized (MyApp.class) {
            if (instance == null) {
                instance = new MyApp ();
            }
        }
    }

    return instance;
}

mais je n'utilise pas partout, j'utilise getContext() et getApplicationContext() où je peux le faire!


0
2018-06-05 13:52



La définition statique du contexte provoquera la fuite de mémoire

Méthode standard pour obtenir un contexte partout:

public class App extends Application {

    public static transient SoftReference<Context> contextReference;

    @Override
    public void onCreate() {
        super.onCreate();
        contextReference = new SoftReference<Context>(getApplicationContext());
    }
}

De cette façon, vous aurez un contexte n'importe où dans le code comme ça :

App.contextReference.get();

Toute autre manière réduira les performances et provoquera la fuite de mémoire

J'espère être utile ...


0
2018-06-22 09:19