20161226

安卓关于SharedPreferences的commit方法和apply的区别

原帖:http://blog.csdn.net/double2hao/article/details/53871640

官网上面对apply的定义为:

1、如果先后apply()了几次,那么会以最后一次apply()的为准。 
2、commit()是把内容同步提交到硬盘的。而apply()先立即把修改提交到内存,然后开启一个异步的线程提交到硬盘,并且如果提交失败,你不会收到任何通知。 
3、如果当一个apply()的异步提交还在进行的时候,执行commit()操作,那么commit()是会阻塞的。而如果commit()的时候,前面的commit()还未结束,这个commit()还是会阻塞的。(所以引起commit阻塞会有这两种原因) 
4、由于SharePreferences在一个程序中的实例一般都是单例的,所以如果你不是很在意返回值的话,你使用apply()代替commit()是无所谓的。

 

apply的源码:

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

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

 

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;
}

代码其实差不多,主要有两点不同: 
1、commit()有返回值,apply()没有返回值。正是验证了官方的解释:apply()失败了是不会报错的。 
2、有一行代码在commit()中是直接执行的,而在apply()中是放到了Runnable中,这行代码意思是等待文件写完:mcr.writtenToDiskLatch.await();

放到Runnable中,其实比较好推测,就是想放到线程池中执行呗,当然这仅仅是我们的推测,我们需要找到具体的代码证明。那么我们就查看调用这个Runnable的enqueueDiskWrite()方法:

private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
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);

// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (SharedPreferencesImpl.this) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}

QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
}

如上,我们可以看到writeToFile()也就是写入文件的逻辑我们会放到writeToDiskRunnable 这个Runnable中,如果没有传递postWriteRunnable进来(也就是commit的情况),那么就会在当前线程执行写入文件操作,而如果传递了postWriteRunnable进来(也就是apply的情况),那么就会把写入文件的逻辑放到线程池中运行。 
这里也是验证了官方的说明:apply()写入文件的操作是异步的,而commit()的写入文件的操作是在当前线程同步执行的。

 

总结:

从源码来分析其实很简单,两者主要区别有两点: 
1、commit()有返回值,apply()没有返回值。apply()失败了是不会报错的。 
2、apply()写入文件的操作是异步的,会把Runnable放到线程池中执行,而commit()的写入文件的操作是在当前线程同步执行的。 
因此当两者都可以使用的时候还是推荐使用apply(),因为apply()写入文件操作是异步执行的,不会占用主线程资源。

 

posted @ 2016-12-26 16:14  别着急慢慢来  阅读(166)  评论(0编辑  收藏  举报