SharedPreferences是Android开发常用配置存储工具类,使用简单,但是要用好还是有一些细节需要我们注意的
-
存放路径
SharedPreferences通过xml文件来存储数据,通过PreferencesManager.getDefaultSharedPreferences()获取的Sp实例对应的xml文件一般为<u>/data/data/包名/shared_prefs/包名_preferences.xml</u>,而通过Context.getSharedPreferences()获取的Sp页都会存储在目录<u>/data/data/包名/shared_prefs/</u>
-
获取到的SharedPreferences都是单例
Sp对象创建后被保存在一个packagePrefs的Map中,而不会重复创建新对象
/*ContextImpl.java*/
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
if (sSharedPrefs == null) {
sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
}
......
sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
}
}
......
return sp;
}
-
SharedPreferences本质是一个存储在内存中的Map容器
Sp对象创建时会将文件内容读入内存,即使在UI线程下使用Sp获取数据性能也是极好的,但这意味着Sp不应存储大量的数据,Sp作为单例对象会常驻内存,增加oom的风险
/*SharedPreferencesImpl.java*/
SharedPreferencesImpl(File file, int mode) {
mFile = file;
......
startLoadFromDisk();
}
private void loadFromDiskLocked() {
......
str = new BufferedInputStream(
new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);
......
}
-
单进程环境下读写是安全的
Sp的get/commit方法都使用了同一对象作为锁保证内存中数据更新的同步安全,Sp对象是单例,在单个进程内的线程中调用get/commit方法都是安全的
/*SharedPreferencesImpl.java*/
public int getInt(String key, int defValue) {
synchronized (this) {//this为SharedPreferencesImpl实例
awaitLoadedLocked();
Integer v = (Integer)mMap.get(key);//从对象mMap获取数据
return v != null ? v : defValue;
}
}
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();//更新内存中数据
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);//更新数据到文件
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
private MemoryCommitResult commitToMemory() {
synchronized (SharedPreferencesImpl.this) {//同步
......
}
return mcr;
}
final Runnable writeToDiskRunnable = new Runnable() {
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr);
}
synchronized (SharedPreferencesImpl.this) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
-
使用apply方法
与commit方法相比,apply方法使用异步方式将数据更新到文件,在单进程的环境下,apply()可以替代commit(),拥有更好的性能
/*SharedPreferencesImpl.java*/
public void apply() {
final MemoryCommitResult mcr = commitToMemory(); //与commit相同马上更新内存
final Runnable awaitCommit = new Runnable() {
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.add(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
public void run() {
awaitCommit.run();
QueuedWork.remove(awaitCommit);
}
};
//异步更新数据到文件
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
}