Question Comment déclarer des variables globales dans Android?


Je crée une application qui nécessite une connexion. J'ai créé l'activité principale et l'activité de connexion.

Dans l'activité principale onCreate méthode j'ai ajouté la condition suivante:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    ...

    loadSettings();
    if(strSessionString == null)
    {
        login();
    }
    ...
}

le onActivityResult La méthode qui est exécutée lorsque le formulaire de connexion se termine ressemble à ceci:

@Override
public void onActivityResult(int requestCode,
                             int resultCode,
                             Intent data)
{
    super.onActivityResult(requestCode, resultCode, data);
    switch(requestCode)
    {
        case(SHOW_SUBACTICITY_LOGIN):
        {
            if(resultCode == Activity.RESULT_OK)
            {

                strSessionString = data.getStringExtra(Login.SESSIONSTRING);
                connectionAvailable = true;
                strUsername = data.getStringExtra(Login.USERNAME);
            }
        }
    }

Le problème est que le formulaire de connexion apparaît parfois deux fois login() méthode est appelée deux fois) et aussi lorsque le clavier du téléphone glisse le formulaire de connexion apparaît à nouveau et je suppose que le problème est la variable strSessionString.

Est-ce que quelqu'un sait comment définir la variable globale afin d'éviter que le formulaire de connexion apparaisse après que l'utilisateur s'est authentifié avec succès?


565
2018-04-02 01:54


origine


Réponses:


J'ai écrit cette réponse en '09 quand Android était relativement nouveau, et il y avait beaucoup de zones pas bien établies dans le développement Android. J'ai ajouté un long addendum au bas de ce post, adressant quelques critiques, et détaillant un désaccord philosophique que j'ai avec l'utilisation de Singletons plutôt que de sous-classer Application. Lisez-le à vos risques et périls.

RÉPONSE ORIGINALE 

Le problème plus général que vous rencontrez est de savoir comment enregistrer l'état sur plusieurs activités et toutes les parties de votre application. Une variable statique (par exemple, un singleton) est une manière commune de Java d'y parvenir. J'ai trouvé cependant qu'une manière plus élégante dans Android est d'associer votre état au contexte de l'application.

Comme vous le savez, chaque activité est également un contexte, qui est une information sur son environnement d'exécution au sens le plus large. Votre application a également un contexte et Android garantit qu'il existera en tant qu'instance unique dans votre application.

La façon de faire est de créer votre propre sous-classe de android.app.Application, puis spécifiez cette classe dans la balise d'application de votre manifeste. Maintenant, Android crée automatiquement une instance de cette classe et la rend disponible pour l'ensemble de votre application. Vous pouvez y accéder depuis n'importe quel context en utilisant le Context.getApplicationContext() méthode (Activity fournit également une méthode getApplication() qui a exactement le même effet). Voici un exemple extrêmement simplifié, avec des mises en garde à suivre:

class MyApp extends Application {

  private String myState;

  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

Cela a essentiellement le même effet que l'utilisation d'une variable statique ou singleton, mais s'intègre assez bien dans le cadre Android existant. Notez que cela ne fonctionnera pas entre les processus (si votre application est l'une des rares à avoir plusieurs processus).

Quelque chose à noter de l'exemple ci-dessus; supposons que nous avions plutôt fait quelque chose comme:

class MyApp extends Application {

  private String myState = /* complicated and slow initialization */;

  public String getState(){
    return myState;
  }
}

Maintenant, cette initialisation lente (comme frapper le disque, frapper le réseau, tout blocage, etc.) sera effectuée chaque fois que l'application est instanciée! Vous pouvez penser, eh bien, ce n'est qu'une seule fois pour le processus et je vais devoir payer le coût de toute façon, non? Par exemple, comme Dianne Hackborn le mentionne ci-dessous, il est tout à fait possible que votre processus soit instancié -juste- pour gérer un événement de diffusion en arrière-plan. Si votre traitement de diffusion n'a pas besoin de cet état, vous avez potentiellement fait toute une série d'opérations compliquées et lentes pour rien. L'instanciation paresseuse est le nom du jeu ici. Ce qui suit est une manière légèrement plus compliquée d'utiliser Application qui a plus de sens pour n'importe quoi mais la plus simple des utilisations:

class MyApp extends Application {

  private MyStateManager myStateManager = new MyStateManager();

  public MyStateManager getStateManager(){
    return myStateManager ;
  }
}

class MyStateManager {

  MyStateManager() {
    /* this should be fast */
  }

  String getState() {
    /* if necessary, perform blocking calls here */
    /* make sure to deal with any multithreading/synchronicity issues */

    ...

    return state;
  }
}

class Blah extends Activity {

  @Override
  public void onCreate(Bundle b){
    ...
    MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
    String state = stateManager.getState();
    ...
  }
}

Bien que je préfère la sous-classe Application à l'utilisation des singletons ici comme solution plus élégante, je préférerais que les développeurs utilisent des singletons si cela est vraiment nécessaire sans penser du tout aux implications de performance et de multithreading de l'association de l'état à la sous-classe Application.

NOTE 1: Aussi, comme anticafe commenté, afin d'attacher correctement votre remplacement de l'application à votre application, une balise est nécessaire dans le fichier manifeste. Encore une fois, voir les docs Android pour plus d'informations. Un exemple:

<application
     android:name="my.application.MyApp" 
     android:icon="..."
     android:label="...">
</application>

NOTE 2: L'utilisateur 608578 demande ci-dessous comment cela fonctionne avec la gestion des cycles de vie des objets natifs. Je ne suis pas au courant de l'utilisation du code natif avec Android dans le moindre, et je ne suis pas qualifié pour répondre à la question de savoir comment cela pourrait interagir avec ma solution. Si quelqu'un a une réponse à cela, je suis prêt à les créditer et mettre les informations dans ce poste pour une visibilité maximale.

ADDENDA:

Comme certaines personnes l'ont noté, c'est ne pas une solution pour persistant état, quelque chose que je devrais peut-être avoir souligné plus dans la réponse originale. C'est à dire. ceci n'est pas destiné à être une solution pour sauvegarder des informations d'utilisateur ou autres qui sont censées être conservées pendant toute la durée de vie des applications. Ainsi, je considère la plupart des critiques ci-dessous liées à des applications étant tuées à tout moment, etc ..., sans objet, car tout ce qui doit être conservé sur le disque ne doit pas être stocké par une sous-classe Application. Il est destiné à être une solution pour stocker un état d'application temporaire et facilement re-créable (si un utilisateur est connecté par exemple) et des composants qui sont mono-instance (gestionnaire de réseau d'application par exemple) (NE PAS singleton!) dans la nature.

Dayerman a eu la gentillesse de souligner un intéressant conversation avec Reto Meier et Dianne Hackborn dans lequel l'utilisation des sous-classes Application est découragée en faveur des modèles Singleton. Somatik a également signalé quelque chose de cette nature plus tôt, même si je ne l'ai pas vu à ce moment-là. En raison des rôles de Reto et Dianne dans le maintien de la plate-forme Android, je ne peux pas de bonne foi recommander d'ignorer leurs conseils. Ce qu'ils disent, va. Je ne suis pas d'accord avec les opinions exprimées en ce qui concerne la préférence pour les sous-classes Singleton plutôt que pour les demandes. Dans mon désaccord, je vais utiliser les concepts les mieux expliqués dans cette explication StackExchange du modèle de conception Singleton, de sorte que je n'ai pas à définir des termes dans cette réponse. J'encourage fortement l'écrémage du lien avant de continuer. Point par point:

Dianne déclare: «Il n'y a pas de raison de sous-classer Application, ce n'est pas différent de faire un singleton ...» Cette première affirmation est incorrecte. Il y a deux raisons principales pour cela. 1) La classe Application offre une meilleure garantie à vie pour un développeur d'applications; il est garanti d'avoir la durée de vie de l'application. Un singleton n'est pas lié de manière EXPLICITE à la durée de vie de l'application (bien que ce soit effectivement). Cela peut ne pas être un problème pour votre développeur d'applications moyen, mais je dirais que c'est exactement le type de contrat que l'API Android devrait offrir, et il offre beaucoup plus de flexibilité au système Android, en minimisant la durée de vie des Les données. 2) La classe Application fournit au développeur de l'application un détenteur d'instance unique pour l'état, ce qui est très différent d'un détenteur d'état singleton. Pour une liste des différences, voir le lien d'explication de Singleton ci-dessus.

Dianne continue, "... juste susceptible d'être quelque chose que vous regretter à l'avenir lorsque vous verrez votre objet Application devenir ce grand désordre de ce qui devrait être une logique d'application indépendante." Ce n'est certainement pas incorrect, mais ce n'est pas une raison pour choisir la sous-classe Singleton over Application. Aucun des arguments de Diane n'indique que l'utilisation d'un Singleton est meilleure qu'une sous-classe Application, tout ce qu'elle tente d'établir, c'est que l'utilisation d'un Singleton n'est pas pire qu'une sous-classe Application, ce que je crois être faux.

Elle poursuit: "Et cela conduit plus naturellement à la façon dont vous devriez gérer ces choses - les initialiser à la demande." Cela ne tient pas compte du fait qu'il n'y a aucune raison de ne pas pouvoir initialiser à la demande en utilisant une sous-classe Application. Encore une fois, il n'y a pas de différence.

Dianne se termine par «Le framework lui-même a des tonnes et des tonnes de singletons pour toutes les petites données partagées qu'il gère pour l'application, comme les caches de ressources chargées, les pools d'objets, etc. Cela fonctionne très bien. Je ne prétends pas que l'utilisation de Singletons ne peut pas fonctionner correctement ou ne constitue pas une alternative légitime. Je soutiens que Singletons n'accorde pas un contrat aussi fort avec le système Android qu'avec une sous-classe Application, et que l'utilisation de Singletons indique généralement une conception inflexible, qui n'est pas facilement modifiée, et conduit à de nombreux problèmes à l'avenir. IMHO, le contrat fort que l'API Android offre aux applications de développement est l'un des aspects les plus attrayants et agréables de la programmation avec Android, et a contribué à l'adoption précoce de développeur qui a conduit la plate-forme Android au succès qu'elle a aujourd'hui. Suggérer d'utiliser Singletons s'éloigne implicitement d'un contrat API fort, et à mon avis, affaiblit le cadre Android.

Dianne a aussi commenté ci-dessous, en mentionnant un inconvénient supplémentaire à l'utilisation des sous-classes d'application, ils peuvent encourager ou faciliter l'écriture de code de performance moins. C'est très vrai, et j'ai édité cette réponse pour souligner l'importance de considérer la perf ici, et de prendre la bonne approche si vous utilisez le sous-classement Application. Comme Dianne l'indique, il est important de se rappeler que votre classe Application sera instanciée chaque fois que votre processus est chargé (peut être plusieurs fois à la fois si votre application s'exécute dans plusieurs processus!) Même si le processus est seulement chargé pour une diffusion en arrière-plan un événement. Il est donc important d'utiliser la classe Application plus comme un référentiel pour les pointeurs vers les composants partagés de votre application plutôt que comme un endroit pour faire un traitement!

Je vous laisse avec la liste suivante des inconvénients à Singletons, comme volé du lien StackExchange précédent:

  • Incapacité d'utiliser des classes abstraites ou d'interface;
  • Incapacité à sous-classer;
  • Haut couplage à travers l'application (difficile à modifier);
  • Difficile à tester (ne peut pas simuler / simuler dans les tests unitaires);
  • Difficulté de paralléliser en cas d'état mutable (nécessite un verrouillage important);

et ajoutez le mien:

  • Contrat de durée de vie imprécis et ingérable inadapté au développement Android (ou à la plupart des autres);

934
2018-04-02 04:34



Créer cette sous-classe

public class MyApp extends Application {
  String foo;
}

Dans AndroidManifest.xml, ajoutez android: nom

Exemple

<application android:name=".MyApp" 
       android:icon="@drawable/icon" 
       android:label="@string/app_name">

151
2017-07-26 20:31



La méthode proposée par Soonil pour conserver un état pour l'application est bonne, mais elle a un point faible: il y a des cas où le système d'exploitation tue tout le processus de l'application. Voici la documentation sur ceci - Processus et cycles de vie.

Considérez un cas - votre application passe en arrière-plan parce que quelqu'un vous appelle (l'application Téléphone est au premier plan maintenant). Dans ce cas && sous d'autres conditions (vérifiez le lien ci-dessus pour ce qu'ils pourraient être) le système d'exploitation peut tuer votre processus de demande, y compris le Application instance de sous-classe. En conséquence, l'état est perdu. Lorsque vous revenez plus tard à l'application, le système d'exploitation va restaurer sa pile d'activité et Application instance de sous-classe, cependant le myState le champ sera null.

AFAIK, le seul moyen de garantir la sécurité de l'état est d'utiliser une sorte de maintien de l'état, par ex. en utilisant un privé pour le fichier d'application ou SharedPrefernces (il utilise finalement un privé pour le fichier d'application dans le système de fichiers interne).


138
2018-01-09 21:49



Juste une note ..

ajouter:

android:name=".Globals"

ou tout ce que vous avez nommé votre sous-classe à la existant  <application> marque. J'ai continué à essayer d'ajouter un autre <application> étiqueter le manifeste et obtiendrait une exception.


25
2018-04-13 21:29



Je ne trouvais pas comment spécifier la balise d'application non plus, mais après beaucoup de Google, il est devenu évident à partir du document manifeste docs: use android: name, en plus de l'icône et du label par défaut dans la section application.

android: nom Nom qualifié complet d'une sous-classe Application implémentée pour l'application. Lorsque le processus d'application est démarré, cette classe est instanciée avant l'un des composants de l'application.

La sous-classe est optionnelle; la plupart des applications n'en auront pas besoin. En l'absence de sous-classe, Android utilise une instance de la classe Application de base.


13
2018-01-14 05:26



Qu'en est-il de garantir la collecte de la mémoire native avec de telles structures globales?

Les activités ont un onPause/onDestroy() méthode appelée destruction, mais la classe Application n'a pas d'équivalent. Quels mécanismes sont recommandés pour garantir que les structures globales (en particulier celles contenant des références à la mémoire native) sont collectées correctement lorsque l'application est supprimée ou que la pile de tâches est placée en arrière-plan?


13
2018-02-25 18:32



Juste vous devez définir un nom d'application comme ci-dessous qui fonctionnera:

<application
  android:name="ApplicationName" android:icon="@drawable/icon">
</application>

5
2018-01-07 05:57



Comme il a été discuté ci-dessus OS pourrait tuer l'APPLICATION sans aucune notification (il n'y a pas d'événement onDestroy) donc il n'y a aucun moyen de sauvegarder ces variables globales.

SharedPreferences pourrait être une solution SAUF si vous avez des variables COMPLEX STRUCTURED (dans mon cas j'avais un tableau integer pour stocker les IDs que l'utilisateur a déjà traités). Le problème avec les SharedPreferences est qu'il est difficile de stocker et récupérer ces structures chaque fois que les valeurs nécessaires.

Dans mon cas, j'avais un service d'arrière-plan afin que je puisse déplacer ces variables et parce que le service a l'événement onDestroy, je pourrais sauvegarder ces valeurs facilement.


4
2017-11-02 08:53



Si certaines variables sont stockées dans sqlite, vous devez les utiliser dans la plupart des activités de votre application. alors Application peut-être le meilleur moyen de l'atteindre. Interrogez les variables de la base de données au démarrage de l'application et stockez-les dans un champ. Vous pouvez ensuite utiliser ces variables dans vos activités.

Alors, trouvez le bon chemin, et il n'y a pas de meilleur moyen.


4
2018-04-20 07:32



Vous pouvez avoir un champ statique pour stocker ce type d'état. Ou placez-le dans le bundle de ressources et restaurez-le sur onCreate (Bundle savedInstanceState). Assurez-vous de bien comprendre le cycle de vie géré par l'application Android (par exemple, pourquoi login () est appelé lors du changement d'orientation du clavier).


3
2018-04-02 02:19



NE PAS  Utilise un autre <application> tag dans le fichier manifeste.Juste faire un changement dans existant <application> tag, ajoutez cette ligne android:name=".ApplicationName" où, ApplicationName sera le nom de votre sous-classe (utilisez pour stocker global) que vous êtes sur le point de créer.

donc, enfin votre LE SEUL ET UNIQUE  <application> tag dans le fichier manifeste devrait ressembler à ceci: -

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.AppCompat.NoActionBar"
        android:name=".ApplicationName"
        >

2
2018-06-22 11:20