版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zgkxzx/article/details/84231218
使用用例
//获得SharedPreferences的实例 sp_name是文件名
SharedPreferences sp = getSharedPreferences("sp_name", Context.MODE_PRIVATE);
//获得Editor 实例
SharedPreferences.Editor editor = sp.edit();
//以key-value形式保存数据
editor.putString("key", "data");
//apply()是异步写入数据
editor.apply();
//commit()是同步写入数据
//editor.commit();
源码分析
** getSharedPreferences **
本来是Context的方法,但是Context是抽象类,具体要看他的实现类 ContextImpl
//存储SP名字与路径文件关系的表结构
private ArrayMap<String, File> mSharedPrefsPaths;
public SharedPreferences getSharedPreferences(String name, int mode) {
// At least one application in the world actually passes in a null
// name. This happened to work because when we generated the file name
// we would stringify it to "null.xml". Nice.
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}
File file;
synchronized (ContextImpl.class) {
//如果表为空 ,创建表
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();
}
//通过名字 得到文件
file = mSharedPrefsPaths.get(name);
if (file == null) {
file = getSharedPreferencesPath(name);
//存入表中
mSharedPrefsPaths.put(name, file);
}
}
//返回SharedPreferences对象
return getSharedPreferences(file, mode);
}
//返回SharedPreferences对象
public SharedPreferences getSharedPreferences(File file, int mode) {
checkMode(mode);
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
sp = cache.get(file);
if (sp == null) {
//返回SharedPreferences的实现类SharedPreferencesImpl
sp = new SharedPreferencesImpl(file, mode);
cache.put(file, 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只是一个接口,我们看看他的实现类SharedPreferencesImpl
我们看看他的构造方法
SharedPreferencesImpl(File file, int mode) {
mFile = file;
//做一个备份文件
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
//从磁盘中加载数据
startLoadFromDisk();
}
//开始加载数据
private void startLoadFromDisk() {
synchronized (this) {
mLoaded = false;
}
//子线程里面执行操作
new Thread("SharedPreferencesImpl-load") {
public void run() {
loadFromDisk();
}
}.start();
}
//从磁盘中加载数据
private void loadFromDisk() {
synchronized (SharedPreferencesImpl.this) {
if (mLoaded) {
return;
}
if (mBackupFile.exists()) {
mFile.delete();
mBackupFile.renameTo(mFile);
}
}
// Debugging
if (mFile.exists() && !mFile.canRead()) {
Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
}
Map map = null;
StructStat stat = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
//文件转化成BufferedInputStream
str = new BufferedInputStream(
new FileInputStream(mFile), 16*1024);
//从流里面读取数据转化成Map
map = XmlUtils.readMapXml(str);
} catch (XmlPullParserException | IOException e) {
Log.w(TAG, "getSharedPreferences", e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
/* ignore */
}
synchronized (SharedPreferencesImpl.this) {
mLoaded = true;
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
notifyAll();
}
}
//通过key在map里面获取Value
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
看看怎么写数据的
public Editor edit() {
synchronized (this) {
awaitLoadedLocked();
}
//返回Editor的实现类
return new EditorImpl();
}
private final Map<String, Object> mModified = Maps.newHashMap();
private boolean mClear = false;
public Editor putString(String key, @Nullable String value) {
synchronized (this) {
//放到表里面
mModified.put(key, value);
return this;
}
}
然后Commit()
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;
}
看看异步提交方法
public void apply() {
//先写入内存中
final MemoryCommitResult mcr = commitToMemory();
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);
}
扩展
** readMapXml **
public static final HashMap<String, ?> readMapXml(InputStream in)
throws XmlPullParserException, java.io.IOException
{
//创建一个XML解析器
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
return (HashMap<String, ?>) readValueXml(parser, new String[1]);
}
//读取XML里面数据
public static final Object readValueXml(XmlPullParser parser, String[] name)
throws XmlPullParserException, java.io.IOException
{
int eventType = parser.getEventType();
do {
if (eventType == parser.START_TAG) {
return readThisValueXml(parser, name, null);
} else if (eventType == parser.END_TAG) {
throw new XmlPullParserException(
"Unexpected end tag at: " + parser.getName());
} else if (eventType == parser.TEXT) {
throw new XmlPullParserException(
"Unexpected text: " + parser.getText());
}
eventType = parser.next();
} while (eventType != parser.END_DOCUMENT);
throw new XmlPullParserException(
"Unexpected end of document");
}
结论:
- SharedPreferences读取xml文件时,会以DOM方式解析(把整个xml文件直接加载到内存中解析),在调用getXXX()方法时取到的是内存中的数据,方法执行时会有个锁来阻塞,目的是等待文件加载完毕,没加载完成之前会wait()。
- SharedPreferences写文件时,如果调用的commit(),会将数据同步写入内存中,内存数据更新,再同步写入磁盘中;如果调用的apply(),会将数据同步写入内存中,内存数据更新,然后异步写人磁盘,也就是说可能写磁盘操作还没有完成就直接返回了。在主线程中建议使用apply(),因为同步写磁盘,当文件较大时,commit()会等到写磁盘完成再返回,可能会有ANR问题。
- SP第一次初始化到读取到数据存在一定延迟,因为需要到文件中读取数据,因此可能会对UI线程流畅度造成一定影响