android9.0中SharedPreferences源码分析(一)
时间:2022-07-24
本文章向大家介绍android9.0中SharedPreferences源码分析(一),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
通过问题的形式来分析SharedPreferences的源码
1.硬盘数据怎么加载到内存的?
- 构造方法执行加载
- 在子线程中加载,因此加载过程不会阻塞 UI
- 本地有数据, 将本地数据 赋值给内存的 mMap,没有的话初始化mMap
final class SharedPreferencesImpl implements SharedPreferences {
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
//1.构造方法执行加载
startLoadFromDisk();
}
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
//2.在子线程中加载,因此加载过程不会阻塞 UI
new Thread("SharedPreferencesImpl-load") {
public void run() {
//这个执行一次
loadFromDisk();
}
}.start();
}
private void loadFromDisk() {
//...
Map<String, Object> map = null;
StructStat stat = null;
Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
new FileInputStream(mFile), 16 * 1024);
// 从本地影片的 xml 加载数据,然后xml 数据转为 map
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
} catch (Throwable t) {
}
synchronized (mLock) {
mLoaded = true;
mThrowable = thrown;
try {
if (thrown == null) {
// 3.本地有数据, 将本地数据 赋值给内存的 mMap
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtim;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
}
} catch (Throwable t) {
}
}
}
}
2.getString() 流程是怎样的?
1.获取 SP 的值的时候 若当前文件过大在加载过程中.则 UI 阻塞
2.在获取 sp 值的时候 会一直判断标记mLoaded,变为 true,true表示从本地加载到内存成功,为false 则 UI 阻塞
3.数据加载成功,若 UI 线程阻塞则唤醒
4.使用 wait/notify 机制
final class SharedPreferencesImpl implements SharedPreferences {
private boolean mLoaded = false;
SharedPreferencesImpl(File file, int mode) {
//2.在获取 sp 值的时候 会一直判断标记mLoaded,变为 true 则表示是否从本地加载到内存成功
// 为 false 则 UI 阻塞
mLoaded = false;
}
@Override
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
//1.获取 SP 的值的时候 若当前文件过大在加载过程中.则 UI 阻塞
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
//3.数据加载成功,若 UI 线程阻塞则唤醒
@GuardedBy("mLock")
private void awaitLoadedLocked() {
//...
while (!mLoaded) {
try {
//4.使用 wait/notify 机制
// 这儿阻塞 UI ,等待加载成功会唤醒
mLock.wait();
} catch (InterruptedException unused) {
}
}
if (mThrowable != null) {
throw new IllegalStateException(mThrowable);
}
}
}
3.数据怎么存储到本地的
这个过程涉及到 3 个 map 之间的数据传递
1. map : mModified
接受用户数据的 map
private final Map<String, Object> mModified = new HashMap<>();
2. map : mapToWriteToDisk
存储到硬盘使用的 map
final Map<String, Object> mapToWriteToDisk;
3. map: mMap
内存中存储数据的 map,初次加载也是将本地xml数据放入 mMap
private Map<String, Object> mMap;
数据传递的过程:
1.在 9.0之前的是: mModified 将数据提交到内存 mMap ,然后 clear()清空自己
2.在 9.0的处理是: mModified 将数据直接提交给mapToWriteToDisk
3.mapToWriteToDisk 将数据 存储到本地
4. commitToMemory() 存储到内存做了什么?
1.使用了两个同步代码块,两个对象锁
2.执行writing到本地的时候,写入过程尚未完成时,又调用了 commitToMemory(),而 且此时没有执行mDiskWritesInFlight--,直接修改 mMap 可能会影响写入结果
3. 数据提交到mapToWriteToDisk
final class SharedPreferencesImpl implements SharedPreferences {
public final class EditorImpl implements Editor {
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
List<String> keysModified = null;
Map<String, Object> mapToWriteToDisk;
//对象锁啊,和get操作同步
synchronized (SharedPreferencesImpl.this.mLock) {
if (mDiskWritesInFlight > 0) {
//深copy
mMap = new HashMap<String, Object>(mMap);
}
//将mMap赋值给mapToWriteToDisk,写入本地 xml 使用
mapToWriteToDisk = mMap;
mDiskWritesInFlight++;
//...
//对象锁啊 和put 操作同步
synchronized (mEditorLock) {
boolean changesMade = false;
//...
// 变量我们的mModified map 里面放入的是将要 存储内存的 sp 数据
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
if (v == this || v == null) {
//...
} else {
//已存在
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
// 放入 map
mapToWriteToDisk.put(k, v);
}
changesMade = true;
//...
}
// 每次内存存储完 清空一下mModified类型的hashMap 的数据
mModified.clear();
if (changesMade) {
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
mapToWriteToDisk);
}
}
}
其他:
synchronized 的详解
深copy
见相关文章
更多内容 欢迎关注公众号
- WCF技术剖析(卷1)之推荐序
- 谈谈基于SQL Server 的Exception Handling[上篇]
- 谈谈WCF中的Data Contract(4):WCF Data Contract Versioning
- 如何在silverlihgt中使用右键
- WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)
- silverlight向服务器post数据类
- WCF技术剖析之十三:序列化过程中的已知类型(Known Type)
- 44 Amazing Silverlight 2.0 Screencasts
- CaseStudy(showcase)类库篇-用agTweener来实现动画效果
- CaseStudy(showcase)数据篇-Loading的制作
- CaseStudy(showcase)数据篇-加载图片
- CaseStudy(showcase)数据篇-从XML中获取数据
- CaseStudy(showcase)布局篇-全屏效果
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(12)-系统日志和异常的处理②
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Hive-1.2.1_03_DDL操作 1.1. Create Database1.2. Drop Database1.3. Use Database2.1. Cre
- Hive-1.2.1_04_DML操作 5.1. Join8.1. 使用案例8.2. Transform实现
- Hive-1.2.1_05_案例操作
- Mysql字符编码利用技巧
- Hive-1.2.1_06_累计报表查询
- Flume-1.8.0_部署与常用案例
- GlassFish 目录穿越漏洞测试过程
- 安装CDH7.1.1
- GIT-SHELL 沙盒绕过(CVE-2017-8386)
- Azkaban-2.5.0-部署与常见案例
- 无字母数字webshell之提高篇
- Sqoop-1.4.7-部署与常见案例
- Hbase-2.0.0_01_安装部署
- 前端黑魔法之远程控制地址栏
- 攻击LNMP架构Web应用的几个小Tricks