Android 访问SharedReference是否应该在UI线程之外完成?

Android 访问SharedReference是否应该在UI线程之外完成?,android,sharedpreferences,android-strictmode,Android,Sharedpreferences,Android Strictmode,随着姜饼的发布,我一直在尝试一些新的API,其中之一就是 我注意到其中一个警告是针对getSharedReferences() 这是警告: StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2 它用于在UI线程上进行的getSharedReferences()调用 SharedReferences访问和更改是

随着姜饼的发布,我一直在尝试一些新的API,其中之一就是

我注意到其中一个警告是针对
getSharedReferences()

这是警告:

StrictMode policy violation; ~duration=1949 ms: android.os.StrictMode$StrictModeDiskReadViolation: policy=23 violation=2
它用于在UI线程上进行的
getSharedReferences()
调用


SharedReferences
访问和更改是否真的应该从UI线程中进行?

访问共享首选项可能需要相当长的时间,因为它们是从闪存中读取的。你读很多书吗?也许您可以使用不同的格式,例如SQLite数据库

但不要使用StrictMode修复您发现的所有问题。或引用文件:

但不要觉得必须修复StrictMode发现的所有问题。特别是,在正常的活动生命周期中,经常需要进行许多磁盘访问。使用StrictMode查找您偶然做的事情。不过,UI线程上的网络请求几乎总是一个问题


我很高兴你已经在玩它了

需要注意的事项:(以懒散的项目符号形式)

  • 如果这是您最糟糕的问题,那么您的应用程序可能处于良好状态。:)不过,写入通常比读取慢,因此请确保使用的是SharedReferenced$Editor.apply()而不是commit()。apply()在GB和async中是新的(但总是安全的,注意生命周期转换)。您可以使用反射在GB+上有条件地调用apply(),在Froyo或更低版本上有条件地调用commit()。我将写一篇博客文章,其中包含如何做到这一点的示例代码
关于装载,不过

  • 加载后,SharedReference是单例的,并在整个进程范围内缓存。因此,您希望尽早加载它,以便在需要它之前将其存储在内存中。(假设它很小,如果您使用的是SharedReferences,一个简单的XML文件,那么它应该很小……)您不想在将来某个用户单击按钮时出错

  • 但是,无论何时调用context.getSharedReferences(…),都会对支持XML文件进行统计,以查看它是否已更改,因此无论如何,您都希望在UI事件期间避免这些统计。一个stat通常应该是快速的(并且经常被缓存),但是yaffs没有太多的并发性(而且很多Android设备都运行在yaffs上……Droid、nexusone等等),所以如果你避开磁盘,你就可以避免被其他正在运行或挂起的磁盘操作卡住

  • 因此,您可能希望在onCreate()期间加载SharedReferences,并重复使用同一实例,避免使用stat

  • 但是,如果您在onCreate()期间不需要您的首选项,那么加载时间会不必要地延迟应用程序的启动,因此通常最好有一个类似FutureTask的子类来启动一个新线程。set()FutureTask子类的值。然后,只要在需要时查找FutureTask的成员并.get()即可。我计划在蜂巢的幕后以透明的方式免费发布。我将尝试发布一些示例代码 展示了这方面的最佳实践


查看Android开发者博客,了解下周将要发布的关于StrictMode相关主题的帖子。

Brad回答的一个微妙之处:即使你在onCreate()中加载了SharedReferences,你可能仍然应该读取后台线程上的值,因为getString()等。直到在Finishs(后台线程)中读取共享文件首选项为止:

edit()也以同样的方式阻止,尽管apply()在前台线程上看起来是安全的


(顺便说一句,很抱歉把这个问题写在这里。我本想把它作为对Brad答案的评论,但我刚刚加入,没有足够的声誉这么做。)

我知道这是一个老问题,但我想分享我的方法。我有很长的阅读时间,并结合使用共享首选项和全局应用程序类:

应用程序类:

public class ApplicationClass extends Application {

    private LocalPreference.Filter filter;

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

    public void setFilter(LocalPreference.Filter filter) {
       this.filter = filter;
    }
}
本地首选项:

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(应用程序中首先调用的活动):

解释的步骤:

  • 主要活动调用getLocalPreferences(this)->这将读取您的首选项,在应用程序类中设置筛选器对象并返回它
  • 当您在应用程序中的其他地方再次调用getLocalPreferences()函数时,它会首先检查该函数在应用程序类中是否不可用,这会更快
  • 注意:始终检查应用程序范围的变量是否与NULL不同,原因->

    应用程序对象不会永远留在内存中,它会被杀死。与人们普遍认为的相反,这款应用不会从头开始重新启动。Android将创建一个新的应用程序对象,并在用户之前的位置启动该活动,让用户产生应用程序从未被杀死的错觉


    如果我没有检查null,我将允许在调用筛选器对象(如果Android从内存中刷取应用程序对象)上的例如getMaxDistance()时抛出null指针。

    SharedReferences类在磁盘上的XML文件中执行一些读写操作,因此就像任何其他IO操作一样,它可能会被阻塞。当前存储在SharedReferences中的数据量会影响API调用所消耗的时间和资源。对于最少量的数据,获取/放置数据只需几毫秒(有时甚至不到一毫秒)。但从专家的角度来看,通过在后台进行API调用来提高性能可能很重要。对于异步SharedReference,我建议签出库。

    我看不出有任何理由从后台线程读取它们。但要写它,我愿意。在启动时,共享首选项文件被加载到内存中,因此访问速度很快,但是写东西可能需要一些时间,因此我们可以使用异步写。这就是shar的commit和apply方法之间的区别
    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;
            }
        }
    
    }
    
    LocalPreference.getLocalPreferences(this);