Android 浅析 SharedPreferences (二) 原理

前言

Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code

概括

分析SharedPreferences的获取生成原理和它的修改原理。并且会看到为什么对于多进程是不安全的。

创建原理

SharedPreferences的获取原理从getSharedPreferences函数开始。所以我们从getSharedPreferences函数为起点开始分析。

ContextImpl

我们知道Context的主要实现从ContextImpl开始,如何传进来的过程忽略。

getSharedPreferences函数返回的是一个SharedPreferences的对象并且是以单例实现的。因为此函数比较重要我们一步步来看。

Step 1.getSharedPreferences()

Part 1.初始化映射对象

ContextImpl 包含了一个比较复杂的内部全局私有变量sSharedPrefs,它的类型是ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>,比较难读,简单点就是从包名映射到preference名字到缓存preferences。
一开始我们先新建一个映射对象,然后获取包名赋予它初值。

Part 2.创建SharedPrefs文件

接下来我们通过一开始传进来的文件名去创建一个SharedPrefs文件,通过getSharedPrefsFile()函数返回一个".xml"的文件对象。然后就会调用new SharedPreferencesImpl(prefsFile, mode)来创建一个SharedPreferencesImpl对象。

SharedPreferencesImpl

SharedPreferences的其中一个重点在于它的创建,构造函数就是获取它对象的地方。

Step 2.SharedPreferencesImpl()

mFile = file;
mBackupFile = makeBackupFile(file);
...
startLoadFromDisk();

构造函数功能是初始化一些变量和调用一些初始函数。
这里有两个变量值得注意,一个是mFile,保存preference的文件对象,另一个是mBackupFile,保存preference的备份文件对象。

首先来看makeBackupFile()函数

Step 3.makeBackupFile()

return new File(prefsFile.getPath() + ".bak");
makeBackupFile从字面意思也很好理解,就是生成一个备份的文件,主要是生成一个prefsFile的备份文件。

初始化的最后是调用startLoadFromDisk()函数进行操作。

Step 4.startLoadFromDisk()

loadFromDiskLocked()
startLoadFromDisk() 的函数功能也很简单,开启一个线程单步调用loadFromDiskLocked()

Step 5.loadFromDiskLocked()

Part 1.查看PrefsFile

loadFromDiskLocked()函数的第一步是查看备份文件是否已经存在,存在则讲原文件删除用备份文件替换。

Part 2.加载PrefsFile

在确认PrefsFile文件可以被读取后会将PrefsFile文件内容加载到一个map里面,确认map不为空后会将它存到全局的mMap里,然后通过notifyAll发出一个通知,通知所有等待初始化完成的线程可以开始运作。

ContextImpl

Step 6.getSharedPreferences()

当创建完一个SharedPreferencesImpl后会将SharedPrefs对象保存到最初的数组里面。最后,如果这不是第一次加载,那么SharedPrefs对象不需要创建,但是会重新调用一次startLoadFromDisk(),让文件保持最新状态。

总结

SharedPreference的创建原理就到此为止了,到此用户就得到了SharedPreference的对象,方便未来的操作。

获取原理

获取的操作都是要在创建SharedPreference之后才可以操作的。

获取的过程我们以获取String为例。

SharedPreferencesImpl

getString()

首先通过SharedPreference对象调用getString方法。此方法有两个参数,一个是key值,也就是我们windows ini配置文件的key值一样,另一个就是默认参数。

首先进来会调用awaitLoadedLocked()来查看加载配置文件是否初始化完成。接着就很简单了,调用全局变量mMap来查找想要的信息。

修改原理

修改的操作都是要在创建SharedPreference之后才可以操作的。

修改的过程我们以修改一个String值为例。

Activity

在我们调用的地方,我们首先通过preference.edit()函数获取SharedPreference的Editor类的对象。

SharedPreferencesImpl

Step 1.edit()

edit()函数返回的是一个新建EditorImpl类的对象。
SharedPreferencesImpl.EditorImpl类里面有一个关键的变量mModified,这是一个Map,它会将以后要修改的值都放到里面去

Activity

Step 2.editor.putString()

这一步很简单,将我们要保存的值存入EditorImpl类里的变量mModified里。

Step 3.editor.commit()

这一步就是调用editor的提交方法了。比较关键。

SharedPreferencesImpl

Step 4.commit()

首先会调用commitToMemory函数返回一个MemoryCommitResult对象。所以我们首先来看下commitToMemory。

Step 5.commitToMemory()

MemoryCommitResult类是一个封装了commitmemory结果的一个类,里面有许多信息。
commitToMemory首先创建一个MemoryCommitResult对象。接着会克隆一个mMap对象。如果有设置监听消息会在这里设置一个值到MemoryCommitResult。接下来我们就会对mMap里面的值进行修改,在修改完成后会把之前我们所进行修改临时保存的全局变量mModified进行清空处理。然后返回出去MemoryCommitResult对象。

在commitToMemory返回一个结果类后会将它当作参数传入enqueueDiskWrite()函数里。

Step 6.enqueueDiskWrite()

enqueueDiskWrite()函数里面执行的是一个异步操作,在外部commit()函数会做一个await操作等待异步的完成。
在这个函数里会开启一个writeToDiskRunnable的线程,该线程做的事情是将传进来的MemoryCommitResult 里的数据写入到文件里。

Step 7.writeToFile()

此函数首先会判断mFile文件是否存在,如果存在就再判断备份文件是否存在,备份文件不在的话就创建一个备份文件,然后删除原文件。
接着创建一个mFile的文件,将数据写入,再添加权限,做完后将备份文件删除。

做完后commit函数的职责就算完了,然后再广播监听者修改完成的消息。

总结

对于修改的原理,在多进程情况下如果两个修改的时机接近那么就很容易导致一方写不进去的问题,是因为会被另一个进程将写入的数据覆盖。这个问题目前的解决方案是不同的进程尽量使用不同的SharedPreference进行存储。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容