android commit apply,Android-SharedPreferences.Editor的commit()和apply()的区别

分析commit()和apply()的区别,首先从源码入手,先看两个方法的源码

apply()

@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) {

}

if (DEBUG && mcr.wasWritten) {

Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration

+ " applied after " + (System.currentTimeMillis() - startTime)

+ " ms");

}

}

};

// 将任务添加到队列中

QueuedWork.addFinisher(awaitCommit);

// 异步执行写入磁盘的任务

Runnable postWriteRunnable = new Runnable() {

@Override

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

// 这里才是真的执行异步的操作,前面的两个Runnable执行被执行了run()方法

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

}

commit()

@Override

public boolean commit() {

long startTime = 0;

if (DEBUG) {

startTime = System.currentTimeMillis();

}

MemoryCommitResult mcr = commitToMemory();

// 在将数据写入内存中后,同步排队写入磁盘中

SharedPreferencesImpl.this.enqueueDiskWrite(

mcr, null /* sync write on this thread okay */);

try {

// 在写入的时候,会导致当前线程等待,直到写入工作完成,即工作队列中的写入任务数为0

mcr.writtenToDiskLatch.await();

} catch (InterruptedException e) {

return false;

} finally {

if (DEBUG) {

Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration

+ " committed after " + (System.currentTimeMillis() - startTime)

+ " ms");

}

}

notifyListeners(mcr);

return mcr.writeToDiskResult;

}

分析

从上述的源码可以看出,SharedPreferences.Editor的commit()和apply()两个方法,在提交到内存的过程中,都是一样的,都是采用同步提交的方式提交到内存,但是提交到磁盘中的过程却有不同,commit是同步提交到磁盘,而apply是异步提交到磁盘;并且apply()并没有返回值,提交失败的时候也不会提示任何失败。并且apply的提交,后续的提交会覆盖之前的提交到内存的内容,只有最后一次提交的内容会被写入磁盘,说明apply()是原子提交的

如果当一个apply()的异步提交还在进行的时候,执行commit()操作,那么commit()是会阻塞的。而如果commit()的时候,前面的commit()还未结束,这个commit()还是会阻塞的。(所以引起commit阻塞会有这两种原因)

apply()引起的主线程阻塞

使用了apply方式异步写sp的时候每次apply()调用都会形成一次提交,每次有系统消息发生的时候(handleStopActivity, handlePauseActivity)都会去检查已经提交的apply写操作是否完成,如果没有完成则阻塞主线程。因为在apply()中,将awaitCommit添加到了一个静态队列中,但是一般情况下这个静态队列中的任务是不会被执行的,但是在QueueWork的waitToFinish()方法被调用的时候,就会执行awaitCommit.run(),而在awaitCommit中会有调用wait()等待阻塞。

SharedPreferences实例的获取

并且同一个进程中,SharedPreferences是单例的,获取SharedPreferences是通过Context获取,看Context的实现类ContextImpl

@Override

public SharedPreferences getSharedPreferences(File file, int mode) {

SharedPreferencesImpl sp;

synchronized (ContextImpl.class) {

final ArrayMap 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");

}

}

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读取数据

如果采用apply()方法提交数据,因为是先将数据同步提交到内存中,然后再异步提交到磁盘中,那么这样就有可能在磁盘中的数据并不是最新的数据。

同进程读取

在同一个进程进行读取数据的时候,因为数据的读写都是针对内存来进行,在写入的时候,会先将数据保存在一个HashMap集合中,读取数据的时候,在同一个进程中是直接从这个HashMap中读取,SharedPreferences中读取数据是针对SharedPreferences类中的HashMap集合,这个集合中的数据是在apply()或者commit()调用提交到内存中的方法的时候,将数据保存到这个集合中

不同进程读取

如果是在不同进程中,SharedPreferences提交数据,因为每个进程的内存是单独的,这样就导致在A进程中写入到内存的数据,在B进程中的内存的数据依然是旧的,所以SharedPreferences在多进程中并不是同步的。

SharedPreferences想要做到进程同步,可以通过设置MODE_MULTI_PROCESS来实现,即SharedPreferences在每次写入的时候都会重新加载磁盘中的数据到内存中,以达到进程间的同步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值