SharedPreferences
@(Android)[SharedPreferences]
[TOC]
结论
- 不需要等结果的就使用apply,而不是commit
- 不要在相同地方多次edit和apply和commit。
- 轻量型别放大key和value,会一直存在内存中
- 用来处理跨进程存取不可靠。
- 文件别太大,读取慢会导致卡主线程。
存储效率对比
(Pixel25模拟器),可能每次不太相同。差别不大。
I/TAG: 31
private void saveSp() {
long startTime = System.currentTimeMillis();
SharedPreferences sharedPreferences=getSharedPreferences("test",MODE_PRIVATE);
SharedPreferences.Editor editor=sharedPreferences.edit();
for (int i = 0; i < 2000; i++) {
editor.putString(i+"key",i+"value");
}
editor.commit();
long endTime = System.currentTimeMillis();
Log.i("TAG",endTime-startTime+"");
}
I/TAG: 14
private void saveSp() {
long startTime = System.currentTimeMillis();
SharedPreferences sharedPreferences=getSharedPreferences("test",MODE_PRIVATE);
SharedPreferences.Editor editor=sharedPreferences.edit();
for (int i = 0; i < 2000; i++) {
editor.putString(i+"key",i+"value");
}
editor.apply();
long endTime = System.currentTimeMillis();
Log.i("TAG",endTime-startTime+"");
}
I/TAG: 5367
private void saveSp() {
long startTime = System.currentTimeMillis();
SharedPreferences sharedPreferences=getSharedPreferences("test",MODE_PRIVATE);
SharedPreferences.Editor editor=sharedPreferences.edit();
for (int i = 0; i < 2000; i++) {
editor.putString(i+"key",i+"value");
editor.commit();
}
long endTime = System.currentTimeMillis();
Log.i("TAG",endTime-startTime+"");
}
I/TAG: 450
private void saveSp() {
long startTime = System.currentTimeMillis();
SharedPreferences sharedPreferences=getSharedPreferences("test",MODE_PRIVATE);
SharedPreferences.Editor editor=sharedPreferences.edit();
for (int i = 0; i < 2000; i++) {
editor.putString(i+"key",i+"value");
editor.apply();
}
long endTime = System.currentTimeMillis();
Log.i("TAG",endTime-startTime+"");
}
源码分析
ContextImpl.java 省略部分无关代码
private ArrayMap<String, File> mSharedPrefsPaths;
private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
//将文件名和文件对应起来放入mSharedPrefsPaths中,没有文件的创建文件。
public SharedPreferences getSharedPreferences(String name,int mode) {
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);
}
}
return getSharedPreferences(file, mode);
}
public SharedPreferences getSharedPreferences(File file, int mode) {
//检查mode,24及24以上不能再使用MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE模式了
checkMode(mode);
//每个文件都对应一个SharedPreferencesImpl ,缓存到sSharedPrefsCache中,注意是静态变量,文件太大太多就会造成内存浪费。
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
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) {
//多进程或者api小于11,每次都是重新加载文件。(伪跨进程存取)
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
SharedPreferencesImpl.java
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
startLoadFromDisk();
}
//开启子线程去加载文件,不会卡的假象。
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
new Thread("SharedPreferencesImpl-load") {
public void run() {
loadFromDisk();
}
}.start();
}
//注意synchronized。
private void loadFromDisk() {
synchronized (mLock) {
if (mLoaded) {
return;
}
if (mBackupFile.exists()) {
mFile.delete();
mBackupFile.renameTo(mFile);
}
}
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);
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
/* ignore */
}
synchronized (mLock) {
//加载成功了
mLoaded = true;
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtime;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
//刷新mLock状态
mLock.notifyAll();
}
}
//注意注意,获取Editor就需要等待了。需要加载文件成功了才能得到此对象。文件如果加载慢,就会堵塞调用线程(平时就是UI线程调用)。导致卡卡卡。
public Editor edit() {
synchronized (mLock) {
awaitLoadedLocked();
}
return new EditorImpl();
}
private void awaitLoadedLocked() {
while (!mLoaded) {
try {
mLock.wait();
} catch (InterruptedException unused) {
}
}
}
public final class EditorImpl implements Editor {
private final Object mLock = new Object();
@GuardedBy("mLock")
private final Map<String, Object> mModified = Maps.newHashMap();
@GuardedBy("mLock")
private boolean mClear = false;
//存储方法
public Editor putString(String key, @Nullable String value) {
synchronized (mLock) {
mModified.put(key, value);
return this;
}
}
//移除方法
public Editor remove(String key) {
synchronized (mLock) {
mModified.put(key, this);
return this;
}
}
//清空方法
public Editor clear() {
synchronized (mLock) {
mClear = true;
return this;
}
}
//当前线程提交,会卡住当前线程等待提交结果
public boolean commit() {
MemoryCommitResult mcr = commitToMemory();
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
//子线程提交,不需要提交结果
public void apply() {
final long startTime = System.currentTimeMillis();
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
public void run() {
try {
mcr.writtenToDiskLatch.await();
}
}
};
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
}
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
QueuedWork.java
在ActivityThread的handleStopActivity方法中
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
会在Activity的onPause(),BroadcastReceiver's onReceive,Service command handling执行吃方法,触发工作队列立即运行。等待工作完成。
QueuedWork.waitToFinish()
网友评论