羁绊和由来
默认值问题.
一般情况下, 如果使用自定义的 SharedPreferences, 并且也未保证 SharedPreferences.get[$Type](String,$Type) 第二个参数和 xml 中定义的默认值保持一致的话, 那么默认值就可能导致一些意想不到的问题.
例如,明明在调用 SharedPreferences时给了默认值, 但是偏偏在PreferenceActivity/Fragment(调用对应的xml)中显示是不同的. 反之亦然.
p.s. 肯定有人会说, 我怎么可能犯这样的错呢, 但是如果参数太多的话, 又因为疏忽, 难免还是会出这样的问题
解决办法
系统默认 SharedPreferences
若使用 PreferenceManager.getDefaultSharedPreferences(Context) 来获取 SharedPreferences 对象, 则在调用它之前, 先调用 PreferenceManager.setDefaultValues(Context, int, boolean).
自定义 SharedPreferences
若使用 Context.getSharedPreferences(String, int) 来获取 SharedPreferences对象,则在调用它之前,先调用 PreferenceManager.setDefaultValues (Context, String, int, int, boolean)
函数解释
PreferenceManager.setDefaultValues 会将 xml 中定义的所有 Preference 默认值设值到SharedPreferences 中. 因此, 不管 SharedPreferences.get 接口中设定的默认值是什么, 它都已经失去了默认值的作用, 除非在 xml 中没有定义默认值. 从而保证了默认值遵从 xml 设定的原则.
补遗
众所周知, SharedPreferences 是通过 Context.getSharedPreferences(String, int) 来获取实例的, 而在Android的App中至少存在1个Context, 即 Application Context, 然后就是 Activity Context, 而 Activity Context 所有函数最终是会调用 Application Context 所对应的函数的.
再来看 Application Context, 如果我没弄错的话, 那么 android.app.ContextImpl 就是它的实现, 其中有代码如下:
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false;
private static final HashMap sSharedPrefs =
new HashMap();
...
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (sSharedPrefs) {
sp = sSharedPrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
sSharedPrefs.put(name, sp);
return sp;
}
}
if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
// If somebody else (some other process) changed the prefs
// file behind our back, we reload it. This has been the
// historical (if undocumented) behavior.
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
}
从这段代码看来, SharedPreferences 可以说就是单例. 因此, 在多个 Activity 之间操作的情况下(例如, 调用 Activity.startActivityForResult(Intent, int) 启动一个Dialog样式的 Activity ), 是无需考虑 SharedPreferences 对象传递的问题的.
需要注意的是, 虽然在 SharedPreferences 的实现 android.app.SharedPreferencesImpl 中, 是用 WeakHashMap 来保存所有 listener 的, 但还是建议 registerOnSharedPreferenceChangeListener & unregisterOnSharedPreferenceChangeListener) 成对调用, 以免造成不必要的麻烦.
参考资料