# Context.getSharedPreferences()

获取 SharedPreferences 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//android.app.Context.java
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}

mBase 是Context的实现类 ContextImpl

//android.app.ContextImpl.java
@Override
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) {
//根据传入的name创建一个File对象
file = getSharedPreferencesPath(name);
mSharedPrefsPaths.put(name, file);
}
}
return getSharedPreferences(file, mode);
}


@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
//先在缓存中查找,如果没有找到对应文件的sp对象,就创建一个新的sp对象
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
sp = cache.get(file);
if (sp == null) {
checkMode(mode);
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
if (isCredentialProtectedStorage()
&& !getSystemService(UserManager.class)
.isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
throw new IllegalStateException("SharedPreferences in credential encrypted "
+ "storage are not available until after user is unlocked");
}
}
//创建一个新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 ,

# SharedPreferences.Editor

sp编辑器 ,用来操作修改 sp 中的数据

# commit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//android.app.SharedPreferencesImpl$EditorImpl


public final class EditorImpl implements Editor {
//存储本次修改的键值
private final Map<String, Object> mModified = new HashMap<>();

@Override
public boolean commit() {
long startTime = 0;


MemoryCommitResult mcr = commitToMemory();

SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
//如果没有执行mcr.setDiskWriteResult();那么mcr.writtenToDiskLatch的state>0,调用await会进入挂起状态,走commit方法时,在enqueueDiskWrite这个方法中已经执行了setDiskWriteResult,所以这里不会阻塞,直接返回结果
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
}
notifyListeners(mcr);
return mcr.writeToDiskResult;
}
}


private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {

//是否是执行同步提交
final boolean isFromSyncCommit = (postWriteRunnable == null);

final Runnable writeToDiskRunnable = new Runnable() {
@Override
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);
}



//写入修改到文件
private void writeToFile(MemoryCommitResult mcr, boolean isFromSyncCommit) {

// 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);

//将本次修改数据写入到文件中
XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);

writeTime = System.currentTimeMillis();

FileUtils.sync(str);

fsyncTime = System.currentTimeMillis();

str.close();
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);


mcr.setDiskWriteResult(true, true);

long fsyncDuration = fsyncTime - writeTime;
mSyncTimes.add((int) fsyncDuration);
mNumSync++;

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, false);
}

从上面的流程可以看出, commit 调用中没有创建子线程去执行,而是在当前线程去执行,所以 commit 会产生 IO 调用,如果写入数据太多,会对主线程由影响,所以最好将 commit 放在子线程中去执行。

.

# apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

@Override
public void apply() {
final long startTime = System.currentTimeMillis();

final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};

QueuedWork.addFinisher(awaitCommit);

Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};

//第二个参数传了一个runnable对象所以不会走commit的流程,会执行
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);


notifyListeners(mcr);
}

从上面可以看到
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
第二个参数传了一个 runnable 对象所以不会走 commit 的流程, enqueueDiskWrite 最后会执行下面的代码
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//sholdDeley 为true,sCanDelay也为true,除非在执行Activity销毁时,会变成false,这里会延迟100ms然后执行MSG_RUN
public static void queue(Runnable work, boolean shouldDelay) {
Handler handler = getHandler();

synchronized (sLock) {
sWork.add(work);

if (shouldDelay && sCanDelay) {
handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
} else {
handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
}
}
}

//android.app.QueueWork.java
private static class QueuedWorkHandler extends Handler {
static final int MSG_RUN = 1;

QueuedWorkHandler(Looper looper) {
super(looper);
}

public void handleMessage(Message msg) {
if (msg.what == MSG_RUN) {
processPendingWork();
}
}
}



private static void processPendingWork() {
long startTime = 0;

synchronized (sProcessingWork) {
LinkedList<Runnable> work;

synchronized (sLock) {
//sWork中就是QueueWork.queue中的runnable对象
work = (LinkedList<Runnable>) sWork.clone();
sWork.clear();

// Remove all msg-s as all work will be processed now
getHandler().removeMessages(QueuedWorkHandler.MSG_RUN);
}

if (work.size() > 0) {
for (Runnable w : work) {
//遍历执行sWork中的任务
w.run();
}
}
}
}

QueueWorkHandler 这个线程中就会执行上面方法 enqueueDiskWrite 中创建的 writeToDiskRunnable 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
final Runnable writeToDiskRunnable = new Runnable() {  
@Override
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};





执行 writeToFile 进行数据写入,

执行 apply 中创建的 postWriteRunnable 对象

1
2
3
4
5
6
7
8

Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};

执行 awaitCommit 任务
1
2
3
4
5
6
7
8
9
final Runnable awaitCommit = new Runnable() {  
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};

执行 mcr.writtenToDiskLatch.await(); ,因为 mcr.writtenToDiskLatch 已经在 wrinteToFile 中执行了 mcr.setDiskWriteResult ,所以 state 已经变成了 0,不会挂起当前的 (QueueWrok) 线程