SharedPreferences 分析 --- 之二

来源:互联网 发布:厦门三套网络电视台 编辑:程序博客网 时间:2024/06/04 17:54

5,提交

5.1 commit

commit方法的逻辑如下,

1, 将数据更新到内存

MemoryCommitResult mcr = commitToMemory();

2, 将内存数据同步到文件

SharedPreferencesImpl.this.enqueueDiskWrite(                mcr, null /* sync write on this thread okay */);

3, 进入等待状态, 直到写入文件的操作完成

mcr.writtenToDiskLatch.await();

4,通知并返回写入结果,

notifyListeners(mcr);return mcr.writeToDiskResult;

1, commitToMemory

内部类EditorImpl 的commitToMemory的主要逻辑如下,

1,当mClear 值为true,则直接清空外部类mMap 中的所有值。

if (mClear) {   if (!mMap.isEmpty()) {      mcr.changesMade = true;     mMap.clear();    }   mClear = false;}

2,逐个遍历mModified中的值,如果值为空或者内部类EditorImpl,则删除该数据,

if (v == this || v == null) {   if (!mMap.containsKey(k)) {        continue;   }   mMap.remove(k);   }

3, 当value值发生改变, 则会更新到mMap

if (mMap.containsKey(k)) {    Object existingValue = mMap.get(k);    if (existingValue != null && existingValue.equals(v)) {        continue;    }}mMap.put(k, v);

4,最后清空mModified变量,

mModified.clear();

2, enqueueDiskWrite

final Runnable writeToDiskRunnable = new Runnable() {public void run() {    synchronized (mWritingToDiskLock) {       //执行文件写入操作[见小节4.3.1]      writeToFile(mcr);     }    synchronized (SharedPreferencesImpl.this) {       mDiskWritesInFlight--;    }    //此时postWriteRunnable为null不执行该方法   if (postWriteRunnable != null) {       postWriteRunnable.run();   } }};final boolean isFromSyncCommit = (postWriteRunnable == null);if (isFromSyncCommit) { //commit方法会进入该分支   boolean wasEmpty = false;   synchronized (SharedPreferencesImpl.this) {   //commitToMemory过程会加1,则wasEmpty=true   wasEmpty = mDiskWritesInFlight == 1;}if (wasEmpty) {  //跳转到上面  writeToDiskRunnable.run();  return;}}//不执行该方法QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);

新建一个子线程调用writeToFile方法保存数据,该方法主要逻辑如下,

1,将mMap数据写入xml文件,

FileOutputStream str = createFileOutputStream(mFile);if (str == null) {   mcr.setDiskWriteResult(false);   return;}XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);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;

2,如果写入成功,则删除备份文件,唤醒等待线程,

mBackupFile.delete();mcr.setDiskWriteResult(true);

3,如果写入失败, 删除未成功写入的文件

if (mFile.exists()) {  if (!mFile.delete()) {      Log.e(TAG, "Couldn't clean up partially-written file " + mFile);  }}mcr.setDiskWriteResult(false);

可见, 每次commit是把全部数据更新到文件, 所以每个文件的数据量必须保证足够精简尽量不要一次写入大量的数据。

5.2 apply

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

可见, apply跟commit的最大区别 在于apply的写入文件操作是在单线程的线程池来完成.

1,apply方法开始的时候, 会把awaitCommit放入QueuedWork;

2,文件写入操作完成, 则会把相应的awaitCommit从QueuedWork中移除.

QueuedWork在这里存在的价值主要是用于在Stop Service, finish BroadcastReceiver过程用于 判定是否处理完所有的异步SP操作.

6,总结

apply 与commit的对比

apply没有返回值, commit有返回值能知道修改是否提交成功

apply是将修改提交到内存,再异步提交到磁盘文件; commit是同步的提交到磁盘文件;

多并发的提交commit时,需等待正在处理的commit数据更新到磁盘文件后才会继续往下执行,从而降低效率; 

而apply只是原子更新到内存,后调用apply函数会直接覆盖前面内存数据,从一定程度上提高很多效率。

获取SP与Editor:

getSharedPreferences()是从ContextImpl.sSharedPrefsCache唯一的SPI对象;

edit()每次都是创建新的EditorImpl对象.

优化建议:

强烈建议不要在sp里面存储特别大的key/value, 有助于减少卡顿/anr

请不要高频地使用apply, 尽可能地批量提交;commit直接在主线程操作, 更要注意了

不要使用MODE_MULTI_PROCESS;

高频写操作的key与高频读操作的key可以适当地拆分文件, 由于减少同步锁竞争;

不要一上来就执行getSharedPreferences().edit(), 应该分成两大步骤来做, 中间可以执行其他代码.

不要连续多次edit(), 应该获取一次获取edit(),然后多次执行putxxx(), 减少内存波动; 经常看到大家喜欢封装方法, 结果就导致这种情况的出现.

每次commit时会把全部的数据更新的文件, 所以整个文件是不应该过大的, 影响整体性能;

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为畅享5s手机打不开网络怎么办 苹果手机突然关机开不了机怎么办 苹果手机掉地上开不了机怎么办 oppo手机突然黑屏开不了机怎么办 苹果6s为什么开不了机怎么办 苹果手机还原出厂设置白苹果怎么办 捡到苹果6s手机怎么办才能自己用 苹果6s玩游戏卡住了怎么办 苹果6s进水换屏后指纹失灵怎么办 苹果手机6s声音按键失灵怎么办 苹果手机触屏失灵怎么办5s 苹果六摔了一下屏幕失灵怎么办 荣耀5c进水屏幕触摸屏失灵怎么办 生活玩家打不了风云蝙蝠岛怎么办啊 企鹅号在注册的时候选错领域怎么办 博士超期学信网的信息被删除怎么办 电脑账号删除后电脑开不了怎么办 lol不小心融错了皮肤怎么办 农村医保交费了没有录入系统怎么办 电动三轮车在泗阳被交警扣了怎么办 环评证实际的设备少报了怎么办 别人盗用了自己的社保卡怎么办 如果有人盗用社保卡信息住院怎么办 丈夫出轨生下孩子妻子该怎么怎么办 l老公偷着儿子消失了怎么办 老婆出轨怀孕现在流了他跑了怎么办 交通银行贷款资金户里面的钱怎么办 新车2个月出现好多小毛病怎么办 地上终末之日尸潮破坏房子怎么办 初一数学期未考试考了77分怎么办 生育服务单再婚婚史情况怎么办 我孩子的数学一直很差怎么办呀! 扶桑花的叶子出斑点发黄怎么办 高二的学生成绩下降特别大怎么办 六个月大的宝宝咳嗽伴有气喘怎么办 昨天奶用力吸今天好疼怎么办 律师把医院的医药费单子丢了怎么办 母亲是个不明事理的人怎么办 华西医院的就诊卡掉了怎么办 整形医院把我脸上疤痕被大了怎么办 牙齿缝里的东西很臭怎么办