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 I a ajouté la condition suivante:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
loadSettings();
if(strSessionString == null)
{
login();
}
...
}
la méthode onActivityResult
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 (la méthode login()
est appelée deux fois) et aussi quand le clavier téléphonique glisse le formulaire de connexion apparaît à nouveau et je suppose que le problème est la variable strSessionString
.
personne Ne sait comment définir la variable globale afin d'éviter de connexion formulaire apparaissant après que l'utilisateur s'authentifie avec succès?
17 réponses
j'ai écrit cette réponse en '09 quand Android était relativement nouveau, et il y avait beaucoup de domaines pas bien établis dans le développement Android. J'ai ajouté un long addendum au bas de ce post, abordant certaines critiques, et détaillant un désaccord philosophique que j'ai avec l'utilisation de Singletons plutôt que l'application de sous-classification. Lire à vos risques et périls.
RÉPONSE ORIGINALE:
Le problème plus général vous rencontrez est de savoir comment sauvegarder l'État à travers plusieurs activités et toutes les parties de votre application. Une variable statique (par exemple, une singleton) est une méthode Java courante pour y parvenir. J'ai trouvé cependant, qu'une façon plus élégante dans Android est d'associer votre état avec le contexte de l'Application.
comme vous le savez, chaque activité est aussi un contexte, c'est-à-dire une information sur son environnement d'exécution au sens le plus large. Votre demande a également un contexte, et Android garantit qu'il existera en tant qu'instance unique à travers votre application.
la façon de faire ceci est de créer votre propre sous-classe de android.App.Application , puis préciser cette classe dans l'étiquette d'application de votre manifeste. Maintenant Android va automatiquement créer une instance de cette classe et la rendre disponible pour l'ensemble de votre application. Vous pouvez y accéder à partir de n'importe quel context
en utilisant la méthode Context.getApplicationContext()
( Activity
aussi fournit une méthode getApplication()
qui a exactement le même effet). Voici un exemple extrêmement simplifié, avec les mises en garde suivantes:
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'un des rares à avoir plusieurs processus).
quelque chose à noter de l'exemple supposons que nous ayons fait quelque chose du genre:
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 ce qui bloque, etc) sera effectuée chaque fois que L'Application est instanciée! Vous pensez peut-être que ce n'est qu'une fois pour toutes et que je devrai payer 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 traiter une émission de fond événement. Si votre traitement de diffusion n'a pas besoin de cet état, vous venez potentiellement de faire toute une série d'opérations compliquées et lentes pour rien. Paresseux instanciation est le nom du jeu ici. Ce qui suit est une façon un peu plus compliquée d'utiliser L'Application qui a plus de sens pour tout sauf le 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 application subclassing à l'utilisation de singletons ici comme solution plus élégante, je préférerais les développeurs utilisent des singletons si vraiment nécessaire sur ne pas penser du tout à travers la performance et les implications de multithreading d'associer l'état avec la sous-classe D'Application.
NOTE 1: aussi comme anticafe a commenté, afin de lier correctement votre application override à votre application une étiquette est nécessaire dans le fichier de 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: user608578 demande ci-dessous comment cela fonctionne avec la gestion des cycles de vie des objets natifs. Je ne suis pas à la hauteur sur l'utilisation du code natif avec Android dans le moindre, et je ne suis pas qualifié pour répondre à la façon dont cela interagirait avec ma solution. Si quelqu'un a une réponse à cela, je suis prêt à leur accorder un crédit et à mettre l'information dans ce post pour une visibilité maximale.
ADDENDUM:
comme certains l'ont noté, c'est pas une solution pour persistant état, quelque chose que j'aurais peut-être dû souligner plus dans la réponse originale. C'est-à-dire: ce n'est pas censé être une solution pour sauver l'utilisateur ou d'autres informations qui est censé persister tout au long de la durée de vie de l'application. Par conséquent, je considère que la plupart des critiques ci-dessous ont trait à des Applications qui sont tuées à tout moment, etc..., moot, que tout ce qui a jamais eu besoin d'être persisté sur le disque ne devrait pas être stocké à travers un Sous-classe d'Application. Il est destiné à être une solution pour stocker l'état d'application temporaire, facilement recréable (si un utilisateur est connecté par exemple) et les composants qui sont une seule instance (application network manager par exemple) ( NOT singleton!) dans la nature.
Dayerman a eu la gentillesse de souligner une intéressante conversation avec Reto Meier et Dianne Hackborn dans laquelle l'utilisation des sous-classes D'Application est découragé en faveur de Singleton patterns. Somatik a également souligné quelque chose de cette nature plus tôt, bien que je ne l'ai pas vu à l'époque. En raison des rôles de Reto et Dianne dans le maintien de la plateforme Android, Je ne peux pas recommander en toute bonne foi d'ignorer leurs conseils. Ce qu'ils disent, va. Je ne suis pas d'accord avec les opinions exprimées au sujet de la préférence accordée à Singleton par rapport aux sous-classes D'Application. Dans mon désaccord, je vais faire usage de concepts mieux expliqués dans ce StackExchange explication du modèle de conception Singleton , de sorte que je n'ai pas à définir les termes de cette réponse. J'encourage vivement à parcourir le lien avant de continuer. Point par point:
Dianne déclare, " Il n'y a aucune raison à la sous-classe de L'Application. Ce n'est pas différent de faire un singleton..."Cette première allégation est incorrecte. Il y a deux raisons principales à cela. 1) la classe Application offre une meilleure garantie à vie pour un développeur d'application; c'est la garantie d'avoir la durée de vie de l'application. Un singleton n'est pas explicitement lié à la durée de vie de l'application (bien qu'il le soit effectivement). Cela peut ne pas être un problème pour votre développeur d'application moyen, mais je dirais que c'est exactement le type de contrat que L'API Android devrait offrir, et il fournit beaucoup plus de flexibilité au système Android ainsi, en minimisant la durée de vie des données associées. 2) La classe Application fournit au développeur d'application un titulaire d'instance unique pour l'état, ce qui est très différent d'un titulaire D'état unique. Pour une liste des différences, voir le lien explication de Singleton ci-dessus.
Dianne continue,"...juste susceptible d'être quelque chose que vous regrettez dans le futur que vous trouvez votre objet D'Application devenant ce grand désordre enchevêtré de ce qui devrait être la logique d'application indépendante."Ce n'est certainement pas incorrect, mais ce n'est pas une raison pour choisir Singleton plutôt que la sous-classe D'Application. Aucun des arguments de Diane fournissent une raison que l'utilisation D'un Singleton est mieux qu'une sous-classe D'Application, tout ce qu'elle tente d'établir est que l'utilisation d'un Singleton n'est pas pire qu'une sous-classe d'Application, qui je crois est faux.
continue - t-elle, "et cela mène plus naturellement à la façon dont vous devriez gérer ces choses-les initialiser sur demande."Cela ne tient pas compte du fait qu'il n'y a aucune raison que vous ne puissiez pas initialiser sur demande en utilisant également une sous-classe D'Application. Encore il n'y a pas de différence.
Dianne conclut avec "le cadre lui-même a des tonnes et des tonnes de singleton pour toutes les petites données partagées qu'il maintient pour l'application, telles que des caches de ressources chargées, des pools d'objets, etc. Il fonctionne très bien."Je ne prétends pas que l'utilisation de Singletons ne peut pas fonctionner correctement ou ne sont pas une alternative légitime. Je soutiens que les Singletons ne fournissent pas un contrat aussi fort avec le système Android que l'utilisation d'une sous-classe D'Application, et en outre que l'utilisation Les Singletons pointent généralement vers un design rigide, qui n'est pas facilement modifiable, et conduit à de nombreux problèmes sur la route. IMHO, le contrat fort de L'API Android offre aux applications de développeur est l'un des aspects les plus attrayants et agréables de la programmation avec Android, et a contribué à l'adoption rapide de développeur qui a conduit la plate-forme Android au succès qu'elle a aujourd'hui. Suggérer l'utilisation de Singletons s'éloigne implicitement d'un contrat API fort, et à mon avis, affaiblit le Cadre Android.
Dianne a également commenté ci-dessous, mentionnant un inconvénient supplémentaire à l'utilisation des sous-classes D'Application, ils peuvent encourager ou faciliter l'écriture de moins de code de performance. C'est très vrai, et j'ai modifié cette réponse pour souligner l'importance de tenir compte de perf ici, et d'adopter la bonne approche si vous utilisez le sous-classement D'Application. Comme le dit Dianne, il est important de se rappeler que votre classe D'Application sera instanciée à chaque fois que temps de chargement de votre processus (peut être plusieurs fois à la fois si votre application s'exécute en plusieurs processus!) même si le processus n'est chargé que pour un événement de diffusion en arrière-plan. Il est donc important d'utiliser la classe d'Application plus comme un référentiel pour les pointeurs partagés composants de votre application plutôt que comme un lieu pour faire le traitement!
je vous laisse avec la liste suivante des inconvénients à Singleton, comme volé du lien StackExchange plus tôt:
- incapacité d'utiliser les classes d'abrégé ou d'interface;
- incapacité à la sous-classe;
- Haut de couplage à travers l'application (difficile de les modifier);
- Difficile à tester (pas de faux/fantaisie dans les tests unitaires);
- difficile à mettre en parallèle dans le cas d'une mutation (nécessite un verrouillage important);
et ajouter le mien:
- manque de clarté et ingérable durée de vie du contrat inadapté pour Android (ou la plupart des autres) de développement;
créer la présente sous-classe
public class MyApp extends Application {
String foo;
}
dans L'AndroidManifest.xml ajouter android:nom
exemple
<application android:name=".MyApp"
android:icon="@drawable/icon"
android:label="@string/app_name">
la façon suggérée par Soonil de garder un État pour l'application est bonne, mais elle a un point faible - il y a des cas où OS tue l'ensemble du processus de demande. Voici la documentation sur ce - processus et cycles de vie .
considérez un cas - votre application passe à l'arrière-plan parce que quelqu'un vous appelle (l'application de téléphone est au premier plan maintenant). Dans ce cas, et sous certaines conditions (vérifier le lien ci-dessus pour ce qui ils pourraient être) L'OS peut tuer votre processus d'application, y compris l'instance de la sous-classe Application
. En conséquence, l'état est perdu. Lorsque vous revenez plus tard à l'application, alors L'OS restaurera sa pile d'activités et l'instance de la sous-classe Application
, cependant le champ myState
sera null
.
AFAIK, la seule façon de garantir la sécurité de l'état est d'utiliser toute sorte de persisting l'état, par exemple en utilisant un privé pour le dossier de demande ou SharedPrefernces
(it utilise éventuellement un private pour le fichier d'application dans le système de fichiers interne).
juste une note ..
ajouter:
android:name=".Globals"
ou peu importe le nom que vous avez donné à votre sous-classe à la balise existante <application>
. Je continuais à essayer d'ajouter une autre étiquette <application>
au manifeste et obtiendrais une exception.
Je n'ai pas pu trouver comment spécifier l'étiquette de l'application non plus, mais après beaucoup de Googling, il est devenu évident à partir du fichier manifeste docs: use android:name, en plus de l'icône par défaut et l'étiquette dans l'application stanza.
android: nom Nom complet d'une sous-classe d'Application mise en œuvre pour l'application. Lorsque le processus d'application est lancé, cette classe est instanciée avant tous les composants de l'application.
la sous-classe est facultative; la plupart des applications n'en ont pas besoin. En l'absence d'une sous-classe, Android utilise une instance de la classe Application de base.
qu'en est-il d'assurer la collecte de la mémoire indigène avec de telles structures globales?
activités ont une onPause/onDestroy()
méthode qui est appelé à la destruction, mais la classe D'Application n'a pas d'équivalents. Quel mécanisme est recommandé pour s'assurer que les structures globales (en particulier celles contenant des références à la mémoire native) sont correctement collectées lorsque l'application est soit tuée, soit la pile de tâches est mise à l'arrière-plan?
il vous suffit de définir un nom d'application comme ci-dessous qui fonctionnera:
<application
android:name="ApplicationName" android:icon="@drawable/icon">
</application>
comme il a été discuté ci-dessus OS pourrait tuer l'APPLICATION sans n'importe quelle notification (il n'y a aucun événement onDestroy) donc il n'y a aucun moyen de sauver ces variables globales.
SharedPreferences pourrait être une solution sauf que vous avez des variables structurées complexes (dans mon cas j'ai eu un tableau entier pour stocker les Id que l'Utilisateur a déjà manipulés). Le problème avec les SharedPreferences est qu'il est difficile de stocker et de récupérer ces structures à chaque fois les valeurs nécessaire.
dans mon cas, j'ai eu un service d'arrière-plan pour pouvoir déplacer ces variables là et parce que le service a l'événement onDestroy, je pourrais enregistrer ces valeurs facilement.
si certaines variables sont stockées dans sqlite et que vous devez les utiliser dans la plupart des activités de votre application. alors peut-être la meilleure façon de l'atteindre. Interrogez les variables de la base de données lorsque l'application a commencé et les stocker 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 chemin.
Vous pouvez avoir un champ statique de conserver ce genre d'état. Ou le mettre dans le paquet de ressources et le restaurer à partir de là sur onCreate(Bundle savedInstanceState). Assurez-vous simplement de bien comprendre le cycle de vie de L'application gérée par Android (par exemple, pourquoi login() est appelé lors d'un changement d'orientation du clavier).
DO N't utilisez une autre étiquette <application>
dans le fichier du Manifeste.Il suffit de faire un changement dans la balise <application>
existante , ajouter cette ligne android:name=".ApplicationName"
où, ApplicationName
sera le nom de votre sous-classe(utiliser pour stocker global) que vous êtes sur le point de créer.
ainsi, finalement votre une et seulement <application>
étiquette dans le fichier de 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"
>
Vous pouvez le faire en utilisant deux approches:
- utilisant la classe d'Application
-
De L'Utilisation Partagée Des Préférences
-
utilisant la classe d'Application
exemple:
class SessionManager extends Application{
String sessionKey;
setSessionKey(String key){
this.sessionKey=key;
}
String getSessisonKey(){
return this.sessionKey;
}
}
vous pouvez utiliser la classe ci-dessus pour implémenter login dans votre activité principale comme ci-dessous. Le Code ressemblera à quelque chose comme ceci:
@override
public void onCreate (Bundle savedInstanceState){
// you will this key when first time login is successful.
SessionManager session= (SessionManager)getApplicationContext();
String key=getSessisonKey.getKey();
//Use this key to identify whether session is alive or not.
}
cette méthode fonctionnera pour le stockage temporaire. Vous ne savez vraiment pas quand le système d'exploitation va tuer l'application, à cause de la mémoire faible. Lorsque votre application est en arrière-plan et que l'utilisateur navigue à travers une autre application qui demande plus de mémoire à exécuter, alors votre application sera tué car le système d'exploitation donne plus de priorité aux processus de premier plan que l'arrière-plan. Par conséquent, votre objet d'application sera null avant que l'utilisateur ne se déconnecte. Donc pour cela, je recommande pour utiliser la deuxième méthode Indiquée ci-dessus.
-
de l'Utilisation partagée des préférences.
String MYPREF="com.your.application.session" SharedPreferences pref= context.getSharedPreferences(MyPREF,MODE_PRIVATE); //Insert key as below: Editot editor= pref.edit(); editor.putString("key","value"); editor.commit(); //Get key as below. SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE); String key= getResources().getString("key");
vous pouvez utiliser Intents , Sqlite , ou les préférences partagées . Quand il s'agit du stockage de médias, comme des documents , des photos et des vidéos , vous pouvez créer les nouveaux fichiers à la place.
sur le résultat de l'activité est appelé avant sur le curriculum vitae. Donc, déplacez la vérification de votre connexion à on resume et votre deuxième connexion peut être bloquée une fois que l'activité secomd a donné un résultat positif. Sur le résumé est appelé à chaque fois donc il n'y a pas de soucis de ne pas être appelé la première fois.
L'approche du sous-classement a également été utilisée dans le cadre de BARACUS. De mon point de vue subclassing Application était destiné à travailler avec les cycles de vie de Android; c'est ce que tout application conteneur fait. Au lieu d'avoir des globals alors, j'enregistre des haricots dans ce contexte et les laisse être injectés dans n'importe quelle classe gérable par le contexte. Chaque cas de haricots injectés est en fait un cas unique.
voir cet exemple pour plus de détails
Pourquoi faire un travail manuel, si vous pouvez avoir beaucoup plus?
class GlobaleVariableDemo extends Application {
private String myGlobalState;
public String getGlobalState(){
return myGlobalState;
}
public void setGlobalState(String s){
myGlobalState = s;
}
}
class Demo extends Activity {
@Override
public void onCreate(Bundle b){
...
GlobaleVariableDemo appState = ((GlobaleVariableDemo)getApplicationContext());
String state = appState.getGlobalState();
...
}
}
vous pouvez créer une classe qui étend Application
classe et ensuite déclarer votre variable comme un champ de cette classe et fournir la méthode getter pour elle.
public class MyApplication extends Application {
private String str = "My String";
synchronized public String getMyString {
return str;
}
}
et ensuite pour accéder à cette variable dans votre activité, Utilisez ceci:
MyApplication application = (MyApplication) getApplication();
String myVar = application.getMyString();