SharePreferences是Android中存储数据的常用功能,
SharePreferences使用的时候一般通过Context的public SharedPreferences getSharedPreferences(String name, int mode)
获得,当然也可以自己指定文件public SharedPreferences getSharedPreferences(File file, int mode)
获得。
public SharedPreferences getSharedPreferences(String name, int mode) {
//4.4以下将null转成字符串,即使是4.4以上name为空
//mSharedPrefsPaths.get(name)也不会崩溃,有专门的null键存储数据,类似HashMap
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}
File file;
//锁,SharePreferences源码操作中很多地方都在用
synchronized (ContextImpl.class) {
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();//name:file键值对缓存,方便下次读取
}
file = mSharedPrefsPaths.get(name);
if (file == null) {
file = getSharedPreferencesPath(name);//根据路径取或创建file
mSharedPrefsPaths.put(name, file);//并缓存
}
}
return getSharedPreferences(file, mode);//根据文件和mode找SharedPreferences
}
public SharedPreferences getSharedPreferences(File file, int mode) {
checkMode(mode);//7.0以上不再支持MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE并抛SecurityException
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {//对缓存操作加锁
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();//package:(file:sharedpreference)键值对,缓存package下的ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>>
sp = cache.get(file);
if (sp == null) {
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) {//多进程操作
sp.startReloadIfChangedUnexpectedly();//可能缓存与实际文件数据不一致,所以重新读一下
}
return sp;
}
SharedPreferencesImpl
SharedPreferencesImpl中有两个内部类,我们能接触到的是EditorImpl它实现了SharedPreferences.Editor
SharedPreferences做存储操作的时候第一步edit()
,代码可以看出每次产生一个新EditorImpl对象,这也是使用不当,存储数据丢失的原因之一
public Editor edit() {
synchronized (this) {
awaitLoadedLocked();
}
return new EditorImpl();//每次返回一个新的Editor,慎
}
EditorImpl所有的存操作,都暂时存在Map里,clear只是一个标志位,数据实际的存储操作在apply、commit中执行。
public final class EditorImpl implements Editor {
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;
}
}
public Editor putStringSet(String key, @Nullable Set<String> values) {
synchronized (this) {
mModified.put(key,
(values == null) ? null : new HashSet<String>(values));
return this;
}
}
public Editor putInt(String key, int value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
public Editor putLong(String key, long value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
public Editor putFloat(String key, float value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
public Editor putBoolean(String key, boolean value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
public Editor remove(String key) {//这里很巧妙,并没有移除对象,为什么呢!看看下面的commitToMemory
synchronized (this) {
mModified.put(key, this);
return this;
}
}
public Editor clear() {//置标志位,实际的操作在后执行
synchronized (this) {
mClear = true;
return this;
}
}
····
省略部分代码
····
}
先看下commit()的即时操作
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();//MemoryCommitResult可理解为builder存储信息用,为避免思路跳跃放到后面
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);//开始存储,null表示后序的Runnable操作
try {
mcr.writtenToDiskLatch.await();//等待结果countDown
//MemoryCommitResult里设置的CountDownLatch(1),当存储结束后调用setDiskWriteResult方法,在此内部countDown
} catch (InterruptedException e) {
return false;
}
notifyListeners(mcr);//回调变化
return mcr.writeToDiskResult;//返回操作结果
}
到此看不出即时性再往里走
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
//操作用的Runnable
final Runnable writeToDiskRunnable = new Runnable() {
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr);
}
synchronized (SharedPreferencesImpl.this) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
final boolean isFromSyncCommit = (postWriteRunnable == null);//无后序操作判定为即时操作
if (isFromSyncCommit) {//是否同步提交的判断
boolean wasEmpty = false;
synchronized (SharedPreferencesImpl.this) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();//主线程内完成
return;
}
}
QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);//线程池操作
}
由于commit()中
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null);
第二个参数为null,所以为同步操作。
看看异步提交apply()
public void apply() {
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {//等待结果的Runnable
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.add(awaitCommit);
Runnable postWriteRunnable = new Runnable() {//后序操作的Runnable,实际就是awaitCommit的操作,多了个队列操作
public void run() {
awaitCommit.run();
QueuedWork.remove(awaitCommit);
}
};
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);//异步的原因
// 效果实际产生前就回调
// Okay to notify the listeners before it's hit disk
// because the listeners should always get the same
// SharedPreferences instance back, which has the
// changes reflected in memory.
notifyListeners(mcr);
}
实际的写文件
private void writeToFile(MemoryCommitResult mcr) {
// Rename the current file so it may be used as a backup during the next read将当前文件删除,重新写入文件
if (mFile.exists()) {
if (!mcr.changesMade) {//文件存在且没有修改,不执行操作
// If the file already exists, but no changes were
// made to the underlying map, it's wasteful to
// re-write the file. Return as if we wrote it
// out.
mcr.setDiskWriteResult(true);
return;
}
if (!mBackupFile.exists()) {//备份文件不在,将当前文件变为备份文件
if (!mFile.renameTo(mBackupFile)) {
Log.e(TAG, "Couldn't rename file " + mFile
+ " to backup file " + mBackupFile);
mcr.setDiskWriteResult(false);
return;
}
} else {//备份文件存在,删除当前文件
mFile.delete();
}
}
// Attempt to write the file, delete the backup and return true as atomically as
// possible. If any exception occurs, delete the new file; next time we will restore
// from the backup.
try {
FileOutputStream str = createFileOutputStream(mFile);//文件输出流
if (str == null) {//获取输出流失败返回
mcr.setDiskWriteResult(false);
return;
}
XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);//将map写到文件
FileUtils.sync(str);//同步流
str.close();//关闭流
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);//给文件设置权限
try {
final StructStat stat = Os.stat(mFile.getPath());
synchronized (this) {//文件的时间戳大小信息,用于多进程操作的判断
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
}
} catch (ErrnoException e) {
// Do nothing
}
// Writing was successful, delete the backup file if there is one.
mBackupFile.delete();//删除备份
mcr.setDiskWriteResult(true);//保存成功
return;
} catch (XmlPullParserException e) {
Log.w(TAG, "writeToFile: Got exception:", e);
} catch (IOException e) {
Log.w(TAG, "writeToFile: Got exception:", e);
}
// Clean up an unsuccessfully written file
if (mFile.exists()) {//当意外发生删除文件
if (!mFile.delete()) {
Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
}
}
mcr.setDiskWriteResult(false);//保存失败
}
保存流程完后,下次使用时
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 {
str = new BufferedInputStream(
new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);//读取出map
} catch (XmlPullParserException | IOException e) {
Log.w(TAG, "getSharedPreferences", e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
/* ignore */
}
synchronized (SharedPreferencesImpl.this) {//将mMap = map数据加载成功
mLoaded = true;
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
notifyAll();
}
}
上面跳过了commitToMemory,它的作用就是构建MemoryCommitResult(可理解为SharePreferences的builder)
private MemoryCommitResult commitToMemory() {
MemoryCommitResult mcr = new MemoryCommitResult();
synchronized (SharedPreferencesImpl.this) {
// We optimistically don't make a deep copy until
// a memory commit comes in when we're already
// writing to disk.
if (mDiskWritesInFlight > 0) {//第一次写disk
// We can't modify our mMap as a currently
// in-flight write owns it. Clone it before
// modifying it.
// noinspection unchecked
mMap = new HashMap<String, Object>(mMap);//将之前文件的内容copy
}
mcr.mapToWriteToDisk = mMap;//将要写入disk的map
mDiskWritesInFlight++;//计数
boolean hasListeners = mListeners.size() > 0;//监听器数量
if (hasListeners) {
mcr.keysModified = new ArrayList<String>();//监听的key
mcr.listeners =
new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());//此map的key:value=listener:object,所以此处用keyset就是Listener集合
}
synchronized (this) {
if (mClear) {//清空意味着清空以前数据,如果一个Editor有清空又有添加操作,则清空以前SharePreferences中的数据,保存此次的添加数据
if (!mMap.isEmpty()) {
mcr.changesMade = true;
mMap.clear();
}
mClear = false;
}
for (Map.Entry<String, Object> e : mModified.entrySet()) {//循环临时存储中的数据,加入将要写入disk的map中
String k = e.getKey();
Object v = e.getValue();
// "this" is the magic value for a removal mutation. In addition,
// setting a value to "null" for a given key is specified to be
// equivalent to calling remove on that key.
if (v == this || v == null) {//remove操作的this在这里,设值为null也会执行移除操作
if (!mMap.containsKey(k)) {
continue;
}
mMap.remove(k);
} else {
if (mMap.containsKey(k)) {
Object existingValue = mMap.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mMap.put(k, v);
}
mcr.changesMade = true;//标志位表示需要写入disk
if (hasListeners) {//将监听的关键字记录
mcr.keysModified.add(k);
}
}
mModified.clear();//临时变量清空
}
}
return mcr;
}
还有一个回调功能
private void notifyListeners(final MemoryCommitResult mcr) {
if (mcr.listeners == null || mcr.keysModified == null ||
mcr.keysModified.size() == 0) {
return;
}
if (Looper.myLooper() == Looper.getMainLooper()) {//本身线程在主线程
for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
final String key = mcr.keysModified.get(i);
for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
if (listener != null) {
listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
}
}
}
} else {//回到主线程,方法可以借用
// Run this function on the main thread.
ActivityThread.sMainThreadHandler.post(new Runnable() {
public void run() {
notifyListeners(mcr);
}
});
}
}
网友评论