Les références statiques sont effacées: l'Android décharge les classes au moment de l'exécution si elles ne sont pas utilisées?

J'ai une question spécifique à la façon dont la classe de chargement / récupération des ordures fonctionne dans Android. Nous sommes tombés sur cette question quelques fois maintenant, et pour autant que je le sache, l'Android se comporte différemment ici d'une JVM ordinaire.

Le problème est le suivant: nous essayons actuellement de réduire les cours singleton dans l'application en faveur d'une seule usine d'usine root unique qui a pour seul but de gérer d'autres classes de gestionnaires. Un gestionnaire de niveau supérieur si vous le souhaitez. Cela nous permet de remplacer facilement les implémentations en tests sans opter pour une solution DI complète, car toutes les activités et services partagent la même référence à cette usine racine.

  • Les cas d'utilisation de DropBoxManager?
  • Comment connecter un appareil Android à un périphérique iOS via BLE (Bluetooth Low Energy)
  • Quels sont les 37 paquets API Java éventuellement encombrés par la décision Oracle v Google de mai 2014?
  • Comment importer / exporter une ressource de chaîne Android vers Excel pour la localisation?
  • Développement de sites Web avec la même API que le mobile
  • OnClickListener on LinearLayout
  • Voici comment cela se présente comme suit:

    public class RootFactory { private static volatile RootFactory instance; @SuppressWarnings("unused") private Context context; // I'd like to keep this for now private volatile LanguageSupport languageSupport; private volatile Preferences preferences; private volatile LoginManager loginManager; private volatile TaskManager taskManager; private volatile PositionProvider positionManager; private volatile SimpleDataStorage simpleDataStorage; public static RootFactory initialize(Context context) { instance = new RootFactory(context); return instance; } private RootFactory(Context context) { this.context = context; } public static RootFactory getInstance() { return instance; } public LanguageSupport getLanguageSupport() { return languageSupport; } public void setLanguageSupport(LanguageSupport languageSupport) { this.languageSupport = languageSupport; } // ... } 

    initialize est appelée une fois, dans Application.onCreate , c'est-à-dire avant toute activité ou service démarré. Maintenant, voici le problème: la méthode getInstance revient parfois comme null – même lorsqu'elle est invoquée sur le même thread! Cela ne semble pas être un problème de visibilité; Au lieu de cela, la réserve de référence statique Singleton au niveau de la classe semble avoir été effacée par le collecteur d'ordures. Peut-être que je suis en train de tirer des conclusions ici, mais cela pourrait-être parce que le récupérateur d'ordures Android ou le mécanisme de chargement de classe peut effectivement décharger des classes lorsque la mémoire devient rare, auquel cas la seule référence à l'instance de singleton disparaîtra-t-elle? Je ne suis pas vraiment profondément dans le modèle de mémoire de Java, mais je suppose que cela ne devrait pas se produire, sinon cette façon courante de mettre en œuvre des singletons ne fonctionnerait pas sur un droit JVM?

    Une idée de la raison pour laquelle cela se produit exactement?

    PS: on peut contourner cela en gardant des références "globales" sur l'instance de l'application unique à la place. Cela s'est révélé fiable lorsqu'il faut garder l'objet autour de toute la durée de vie d'une application.

    METTRE À JOUR

    Apparemment, mon utilisation de la volatilité a provoqué une certaine confusion. Mon intention était de s'assurer que l'état actuel de la référence statique soit toujours visible pour tous les threads qui l'accèdent. Je dois le faire parce que je suis à la fois l'écriture et la lecture de cette référence à partir de plus d'un thread: dans une application ordinaire exécutée uniquement dans le thread d'application principal, mais dans un test d'instrumentation, où les objets sont remplacés par des simulacres, je l'écris à partir du Fil d'instrumentation et lisez-le sur le thread UI. Je pourrais également synchroniser l'appel à getInstance , mais c'est plus cher car il faut revendiquer un verrou d'objet. Voir Qu'est-ce qu'un moyen efficace de mettre en œuvre un modèle singleton en Java? Pour une discussion plus détaillée à ce sujet.

  • "Service MeasurementBrokerService est utilisé" s'affiche dans mon processus de demande
  • Barre d'état transparente et barre d'action Android
  • Android - Comment passer HashMap <String, String> entre les activités?
  • Codage de chaînes Android et conversion de l'entité html
  • Obtenez de la valeur (String) de ArrayList <ArrayList <String >> (); En Java
  • Widget personnalisé utilisant LinearLayout ne pas obtenir onDraw ()
  • 4 Solutions collect form web for “Les références statiques sont effacées: l'Android décharge les classes au moment de l'exécution si elles ne sont pas utilisées?”

    Je n'ai jamais vu dans ma vie un membre de données statiques déclaré volatile . Je ne suis même pas sûr de ce que cela signifie.

    Les membres de données statiques existeront jusqu'à ce que le processus soit terminé ou jusqu'à ce que vous les débarrassez (par exemple, null la référence statique). Le processus peut être résilié une fois que toutes les activités et les services sont fermement fermés par l'utilisateur (p. Ex., Bouton BACK) et votre code (par exemple, stopService() ). Le processus peut être terminé même avec des composants en direct si Android est désespérément court sur la RAM, mais cela est plutôt inhabituel. Le processus peut être terminé avec un service en direct si Android pense que votre service a été en arrière-plan trop long, bien qu'il puisse redémarrer ce service en fonction de votre valeur de retour depuis onStartCommand() .

    Les classes ne sont pas déchargées, période, à la fin de la fin de la procédure.

    Pour aborder l'autre des points de @ sergui, les activités peuvent être détruites, l'état de l'instance étant stocké (même s'il s'agit d'une RAM, et non d'un «stockage fixe»), pour libérer de la RAM. Android aura tendance à le faire avant de terminer les processus actifs, mais si cela détruit la dernière activité pour une procédure et qu'il n'y a pas de services en cours d'exécution, ce processus sera un candidat principal pour la fin de session.

    La seule chose particulièrement étrange à propos de votre implémentation est votre utilisation de la volatile .

    Tant vous (@Matthias) que Mark Murphy (@CommonsWare) sont corrects dans ce que vous dites, mais le gist semble perdu. (L'utilisation de volatile est correcte et les classes ne sont pas déchargées.)

    Le point crucial de la question est de savoir où l' initialize est appelée.

    Voici ce que je pense se produire:

    • Vous appelez initialiser à partir d'une Activity
    • Android a besoin de plus de mémoire, tue tout le Process
    • Android redémarre l' Application et la meilleure Activity
    • Vous appelez getInstance qui renverra null , car l' initialize n'a pas été appelée

    Corrige moi si je me trompe.

    Les références statiques sont effacées chaque fois que le système en ressemble et que votre application n'est pas de niveau supérieur (l'utilisateur ne l'exécute pas explicitement). Chaque fois que votre application est minimisée et que le système d'exploitation désire plus de mémoire, il supprimera votre application ou la sérialise sur un stockage fixe pour une utilisation ultérieure, mais dans les deux cas, les variables statiques sont effacées. En outre, chaque fois que votre application obtient une erreur de Force Close , toutes les statiques sont également effacées. Dans mon expérience, j'ai vu qu'il est toujours préférable d'utiliser des variables dans l'objet Application que les variables statiques.

    J'ai vu un comportement étrange semblable avec mon propre code impliquant la disparition de variables statiques (je ne pense pas que ce problème ait quelque chose à voir avec le mot-clé volatil). En particulier, cela s'est produit lorsque j'ai initialisé un cadre de journalisation (ex. Crashlytics, log4j), puis, après une certaine période d'activité, il semble qu'il ne soit pas initialisé. L'enquête a montré que cela se produit après que l'OS appelle sur onSaveInstanceState(Bundle b) .

    Vos variables statiques sont détenues par Classloader qui est contenu dans le processus de votre application. Selon Google:

    Une fonctionnalité inhabituelle et fondamentale d'Android est que la durée de vie d'une procédure d'application n'est pas directement contrôlée par l'application elle-même. Au lieu de cela, il est déterminé par le système grâce à une combinaison des parties de l'application que le système sait courir, de l'importance de ces choses pour l'utilisateur et de la quantité totale de mémoire disponible dans le système.

    http://developer.android.com/guide/topics/processes/process-lifecycle.html

    Ce qui signifie pour un développeur est que vous ne pouvez pas vous attendre à ce que les variables statiques restent initialisées indéfiniment. Vous devez compter sur un mécanisme différent pour la persistance.

    Une solution de contournement que j'ai utilisée pour que mon cadre de journalisation soit initialisé soit pour toutes mes activités afin d'étendre une classe de base où j'ai onCreate et vérifier l'initialisation et la réinitialiser si nécessaire.

    Je pense que la solution officielle consiste à utiliser le onSaveInstanceState(Bundle b) pour persévérer tout ce dont votre activité a besoin plus tard, puis réinitialiser dans onCreate(Bundle b) lorsque b != null .

    Google l'explique mieux:

    http://developer.android.com/training/basics/activity-lifecycle/recreating.html

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.