web-dev-qa-db-fra.com

L'accès à SharedPreferences doit-il être effectué à partir du thread d'interface utilisateur?

Avec la sortie de Gingerbread, j'ai expérimenté certaines des nouvelles API, dont l'une est StrictMode .

J'ai remarqué que l'un des avertissements concerne getSharedPreferences().

Voici l'avertissement:

StrictMode policy violation; ~duration=1949 ms: Android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2

et il est donné pour un appel getSharedPreferences() en cours sur le thread d'interface utilisateur.

SharedPreferences l'accès et les modifications doivent-ils vraiment être effectués à partir du thread d'interface utilisateur?

107
cottonBallPaws

Je suis content que tu joues déjà avec!

Quelques choses à noter: (sous forme de puce paresseuse)

  • si c'est le pire de vos problèmes, votre application est probablement au bon endroit. :) Les écritures sont généralement plus lentes que les lectures, alors assurez-vous d'utiliser SharedPreferenced $ Editor.apply () au lieu de commit (). apply () est nouveau dans GB et async (mais toujours sûr, attention aux transitions du cycle de vie). Vous pouvez utiliser la réflexion pour appeler conditionnellement apply () sur GB + et commit () sur Froyo ou en dessous. Je ferai un article de blog avec un exemple de code sur la façon de procéder.

En ce qui concerne le chargement, cependant ...

  • une fois chargées, les préférences partagées sont des singletons et mises en cache à l'échelle du processus. vous voulez donc le charger le plus tôt possible afin de l'avoir en mémoire avant d'en avoir besoin. (en supposant qu'il soit petit, comme il se doit si vous utilisez SharedPreferences, un simple fichier XML ...) Vous ne voulez pas le blâmer à l'avenir, certains utilisateurs cliquent sur un bouton.

  • mais chaque fois que vous appelez context.getSharedPreferences (...), le fichier XML de sauvegarde est stat'd pour voir s'il est modifié, vous voudrez donc éviter ces statistiques lors des événements de l'interface utilisateur. Une statistique devrait normalement être rapide (et souvent mise en cache), mais yaffs n'a pas beaucoup de concurrence (et beaucoup d'appareils Android fonctionnent sur yaffs). Droid, Nexus One , etc.) donc si vous évitez le disque, vous évitez de rester coincé derrière d'autres opérations de disque en cours ou en attente.

  • vous voudrez donc probablement charger les SharedPreferences pendant votre onCreate () et réutiliser la même instance, en évitant la statistique.

  • mais si vous n'avez de toute façon pas besoin de vos préférences pendant onCreate (), ce temps de chargement retarde inutilement le démarrage de votre application, il est donc généralement préférable d'avoir quelque chose comme une sous-classe FutureTask <SharedPreferences> qui lance un nouveau thread vers .set () la valeur des sous-classes FutureTask. Ensuite, recherchez simplement le membre de votre FutureTask <SharedPreferences> chaque fois que vous en avez besoin et .get (). Je prévois de rendre cela gratuit dans les coulisses de Honeycomb, de manière transparente. Je vais essayer de publier un exemple de code qui montre les meilleures pratiques dans ce domaine.

Consultez le Android Developers pour les articles à venir sur des sujets liés à StrictMode dans les semaines à venir.

178
Brad Fitzpatrick

Une subtilité à propos de la réponse de Brad: même si vous chargez les SharedPreferences dans onCreate (), vous devriez probablement toujours lire les valeurs sur le thread d'arrière-plan car getString () etc. se bloque jusqu'à ce que la préférence de fichier partagé se termine (sur un thread d'arrière-plan):

public String getString(String key, String defValue) {
    synchronized (this) {
        awaitLoadedLocked();
        String v = (String)mMap.get(key);
        return v != null ? v : defValue;
    }
}

edit () bloque également de la même manière, bien que apply () semble être sûr sur le thread de premier plan.

(BTW désolé de mettre cela ici. J'aurais mis cela comme un commentaire à la réponse de Brad, mais je viens de rejoindre et je n'ai pas assez de réputation pour le faire.)

4
Tom O'Neill

Je sais que c'est une vieille question mais je veux partager mon approche. J'ai eu de longs temps de lecture et j'ai utilisé une combinaison de préférences partagées et de la classe d'application globale:

ApplicationClass:

public class ApplicationClass extends Application {

    private LocalPreference.Filter filter;

    public LocalPreference.Filter getFilter() {
       return filter;
    }

    public void setFilter(LocalPreference.Filter filter) {
       this.filter = filter;
    }
}

Préférence locale:

public class LocalPreference {

    public static void saveLocalPreferences(Activity activity, int maxDistance, int minAge,
                                            int maxAge, boolean showMale, boolean showFemale) {

        Filter filter = new Filter();
        filter.setMaxDistance(maxDistance);
        filter.setMinAge(minAge);
        filter.setMaxAge(maxAge);
        filter.setShowMale(showMale);
        filter.setShowFemale(showFemale);

        BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
        babysitApplication.setFilter(filter);

        SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
        securePreferences.edit().putInt(Preference.FILER_MAX_DISTANCE.toString(), maxDistance).apply();
        securePreferences.edit().putInt(Preference.FILER_MIN_AGE.toString(), minAge).apply();
        securePreferences.edit().putInt(Preference.FILER_MAX_AGE.toString(), maxAge).apply();
        securePreferences.edit().putBoolean(Preference.FILER_SHOW_MALE.toString(), showMale).apply();
        securePreferences.edit().putBoolean(Preference.FILER_SHOW_FEMALE.toString(), showFemale).apply();
    }

    public static Filter getLocalPreferences(Activity activity) {

        BabysitApplication babysitApplication = (BabysitApplication) activity.getApplication();
        Filter applicationFilter = babysitApplication.getFilter();

        if (applicationFilter != null) {
            return applicationFilter;
        } else {
            Filter filter = new Filter();
            SecurePreferences securePreferences = new SecurePreferences(activity.getApplicationContext());
            filter.setMaxDistance(securePreferences.getInt(Preference.FILER_MAX_DISTANCE.toString(), 20));
            filter.setMinAge(securePreferences.getInt(Preference.FILER_MIN_AGE.toString(), 15));
            filter.setMaxAge(securePreferences.getInt(Preference.FILER_MAX_AGE.toString(), 50));
            filter.setShowMale(securePreferences.getBoolean(Preference.FILER_SHOW_MALE.toString(), true));
            filter.setShowFemale(securePreferences.getBoolean(Preference.FILER_SHOW_FEMALE.toString(), true));
            babysitApplication.setFilter(filter);
            return filter;
        }
    }

    public static class Filter {
        private int maxDistance;
        private int minAge;
        private int maxAge;
        private boolean showMale;
        private boolean showFemale;

        public int getMaxDistance() {
            return maxDistance;
        }

        public void setMaxDistance(int maxDistance) {
            this.maxDistance = maxDistance;
        }

        public int getMinAge() {
            return minAge;
        }

        public void setMinAge(int minAge) {
            this.minAge = minAge;
        }

        public int getMaxAge() {
            return maxAge;
        }

        public void setMaxAge(int maxAge) {
            this.maxAge = maxAge;
        }

        public boolean isShowMale() {
            return showMale;
        }

        public void setShowMale(boolean showMale) {
            this.showMale = showMale;
        }

        public boolean isShowFemale() {
            return showFemale;
        }

        public void setShowFemale(boolean showFemale) {
            this.showFemale = showFemale;
        }
    }

}

MainActivity (activité qui est appelée en premier dans votre application):

LocalPreference.getLocalPreferences(this);

Étapes expliquées:

  1. L'activité principale appelle getLocalPreferences (this) -> ceci lira vos préférences, définira l'objet filtre dans votre classe d'application et le retournera.
  2. Lorsque vous appelez à nouveau la fonction getLocalPreferences () ailleurs dans l'application, elle vérifie d'abord si elle n'est pas disponible dans la classe d'application, ce qui est beaucoup plus rapide.

REMARQUE: vérifiez TOUJOURS si une variable large de l'application est différente de NULL, raison -> http://www.developerphil.com/dont-store-data-in-the-application-object/

L'objet d'application ne restera pas en mémoire pour toujours, il sera tué. Contrairement à la croyance populaire, l'application ne sera pas redémarrée à partir de zéro. Android créera un nouvel objet Application et démarrera l'activité où l'utilisateur était auparavant pour donner l'illusion que l'application n'a jamais été tuée en premier lieu.

Si je ne vérifiais pas null, j'autoriserais un nullpointer à être lancé lors de l'appel par exemple getMaxDistance () sur l'objet filtre (si l'objet d'application a été glissé de la mémoire par Android)

1
Jdruwe