深入解析Android-SharedPreferences源码

概述

SharedPreferences(简称SP)是Android中常用的数据存储方式,SP采用key-value(键值对)形式,主要用于轻量级的数据存储,尤其适合保存应用的配置参数,但不建议使用SP来存储大规模的数据,可能会降低性能。

SP采用XML文件格式来保存数据,该文件位于 /data/data/<packageName>/shared_prefs/

使用示例

// 加载SP文件数据,“my_prefs”为文件名
SharedPreferences sp = getSharedPreferences("my_prefs", Context.MODE_PRIVATE);
// 保存数据
Editor editor = sp.edit();
editor.putString("blog", "www.xiaox.com");
// 提交数据:同步方式,有返回值表示数据保存是否成功
boolean result = editor.commit(); 
// 提交数据:异步方式,没有返回值// 
editor.apply()
// 读取数据
String blog = sp.getString("blog", "");

my_prefs.xml文件内容:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>    
    <string name="blog">www.xiaox.com</string>
</map>

架构

类图

说明:SharedPreferences与Editor只是两个接口,SharedPreferencesImpl和EditorImp分别实现了对应的接口。另外,ContextImpl记录着SharedPreferences的重要数据,如下:

  • sSharedPrefsCache:以包名为key,二级key是SP文件,以SharedPreferencesImp为value的嵌套map结构,sSharedPrefsCache是静态成员变量,每个进程只有唯一的一份,且由ContextImpl.class锁保护。
  • mSharedPrefsPaths:记录所有的SP文件,以文件名为key,具体文件为value的map结构。
  • mPreferencesDir:是值SP所在目录,即 /data/data/<packageName>/shared_prefs/

工作流程

说明

  1. putXxx()操作:把数据写入到EditorImpl.mModified;
  2. apply()或者commit()操作: a. 先调用 commitToMemory(),将数据同步到SharedPreferencesImpl的mMap,并保存到MemoryCommitResult的 mapToWriteToDisk; b. 再调用 enqueueDiskWrite(),写入到磁盘文件;在这之前把原有数据保存到 .bak后缀的文件,用于在写磁盘的过程出现任何异常可恢复数据。
  3. getXxx()操作:从SharedPreferencesImpl.mMap读取数据。

源码分析(API 28)

获取SharedPreferences

可以通过 Activity.getPreferences(mode)PreferenceManager.getDefaultSharedPreferences(context)或者 Context.getSharedPreferences(name,mode)来获取SharedPreferences实例, 最终调用的是ContextImpl的getSharedPreferences(name, mode)。

ContextImpl#getSharedPreferences(name, mode)

class ContextImpl extends Context {
   
	@GuardedBy("ContextImpl.class")    
		     private ArrayMap<String, File> mSharedPrefsPaths;
	// ...
	@Override    
		    public SharedPreferences getSharedPreferences(String name, int mode) {
   
		// ...
		File file;
		synchronized (ContextImpl.class) {
   
			if (mSharedPrefsPaths == null) {
   
				mSharedPrefsPaths = new ArrayMap<>();
			}
			// 先从mSharedPrefsPaths查询是否存在相应文件            
			file = mSharedPrefsPaths.get(name);
			if (file == null) {
   
				// 如果文件不存在,则创建新的文件                
				file = getSharedPreferencesPath(name);
				// 将新创建的文件保存到mSharedPrefsPaths,以文件名为key                
				mSharedPrefsPaths.put(name, file);
			}
		}
		return getSharedPreferences(file, mode);
	}
	@Override    
	    public File getSharedPreferencesPath(String name) {
   
		return makeFilename(getPreferencesDir(), name + ".xml");
	}
	private File getPreferencesDir() {
   
		synchronized (mSync) {
   
			if (mPreferencesDir == null) {
   
				// 创建目录/data/data/<packageName>/shared_prefs/                
				mPreferencesDir = new File(getDataDir(), "shared_prefs");
			}
			return ensurePrivateDirExists(mPreferencesDir);
		}
	}
}

ContextImpl#getSharedPreferences(file, mode)

@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
   
	SharedPreferencesImpl sp;
	synchronized (ContextImpl.class) {
   
		final ArrayMap<File, SharedPreferencesImpl> 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");
				}
			}
			// 创建SharedPreferencesImpl            
			sp = new SharedPreferencesImpl(file, mode);
			cache.put(file, sp);
			return sp;
		}
	}
	// 指定多进程模式,则当文件被其他进程改变是,则会重新加载    
	if ((mode & Context.MODE_MULTI_PROCESS) 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值