android sharedpreferences加密,让源码告诉你:Android 不要滥用 SharedPreferences(上)

链接:https://www.jianshu.com/p/5fcef7f68341

前言

本文不是与大家一起探讨关于SharedPreferences的基本使用,而是结合源码的角度分析对 SharedPreferences 使用不当可能引发的“严重后果”以及该如何正确的使用 SharedPreferences。

SharedPreferences 是 Android 平台为应用开发者提供的一个轻量级的存储辅助类,用来保存应用的一些常用配置,它提供了 putString()、putString(Set)、putInt()、putLong()、putFloat()、putBoolean() 六种数据类型。数据最终是以 XML 形式进行存储。在应用中通常做一些简单数据的持久化存储。SharedPreferences 作为一个轻量级存储,所以就限制了它的使用场景,如果对它使用不当可能会引发“严重后果”。那么让我们从源码角度出发(基于 API Level 28)吧。

1、 SharedPreferences 文件保存位置

SharedPreferences config = context.getSharedPreferences("config", Context.MODE_PRIVATE);String value = config.getString("key", "default");

通过 Context 的 getSharedPreferences() 方法得到 SharedPreferences 对象,这里实际调用的是 ContextImpl.getSharedPreferences() 方法。

@Overridepublic SharedPreferences getSharedPreferences(String name, int mode){//mBase实际类型是 ContextImplreturn mBase.getSharedPrefenences(name, mode);}

mBase 的实际类型是 ContextImpl(不熟悉的朋友,可以去看下 Activity 的创建过程,在 ActivityThread 中)。

ContextImpl 中 getSharedPreferences(String name, int mode) 调用过程如下:

@Overridepublic SharedPreferences getSharedPreferences(String name, int mode) {if (mPackageInfo.getApplicationInfo().targetSdkVersion

File file;synchronized (ContextImpl.class) {if (mSharedPrefsPaths == null) {//mSharedPrefsPaths维护文件名name和文件File 的映射关系//这个在较早版本中不存在mSharedPrefsPaths = new ArrayMap<>();}//通过文件名name获取对应的文件Filefile = mSharedPrefsPaths.get(name);if (file == null) {//SharedPreferences文件目录创建过程file = getSharedPreferencesPath(name);mSharedPrefsPaths.put(name, file);}}//根据File创建SharedPreferencesreturn getSharedPreferences(file, mode);}

代码中标注了详细的注释,这里主要维护了 SharedPreferences 文件名 name 和文件 File 的映射关系,既根据文件名 name 得到文件 File,每个 Activity 都会包含一个 ContextImpl 对象,mSharedPrefsPaths 是它的成员变量,既仅在当前对象有效。这里重点跟踪下 SharedPreferences 文件的保存目录,SharedPreferences 文件路径创建过程:

/*** 根据文件名创建File对象*/@Overridepublic File getSharedPreferencesPath(String name){return makeFilename(getPreferencesDir(), name+".xml");}

getPreferencesDir 方法如下:

@Overrideprivate File getPreferencesDir(){synchronized(mSync){if(mPreferencesDir == null){//创建SharedPreferences文件保存目录//getDataDir返回:/data/data/packageName/mPreferencesDir = new File(getDataDir(), "shared_prefs");}//确保应用私有文件目录已经存在return ensurePrivateDirExists(mPreferencesDir);}}

从这里可以看出 SharedPreferences 文件的存储位置是在应用程序包名下 shared_prefs 目录内。

这里需要注意的是文件名 name 不能是路径形式如:“/config”,如下将会抛出异常:

@overrideprivate File makeFilename(File base, String name){if(name.indexOf(File.separatorChar) < 0){//SharedPreferences文件名中如果包含“/”字符将会抛出异常return new File(base, name);}throw new IllegalArgumentException("File " + name + " contains a path separator" );}

跟踪到这里,SharedPreferences 的文件保存路径我们就算是找到了。这一步中主要通过文件名 name 创建对应文件 File 对象。并且会将其缓存在 ContextImpl 的 Map(mSharedPerfsPaths)容器中。接着我们看 SharedPreferences 的创建过程。

2、SharedPreferences 创建过程

@Overridepublic SharedPreferences getSharedPreferences(File file, int mode) {SharedPreferencesImpl sp;synchronized (ContextImpl.class) {//得到用于缓存SharedPreferences的Map容器//该Map容器在ContextImpl单例方式声明final ArrayMap cache = getSharedPreferencesCacheLocked();sp = cache.get(file);if (sp == null) {//Android N之后不在支持MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLEcheckMode(mode);//Android 7.0之后的文件级加密相关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");}}//SharedPreferences首次创建,实际类型是SharedPreferencesImpl//SharedPreferences只是一个接口,定义了操作的基本API。//真正实现是在SharedPreferencesImpl中sp = new SharedPreferencesImpl(file, mode);//保存在Map容器中,该Map容器为单例cache.put(file, sp);return sp;}}if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {//MODE_MULTI_PROCESS的加载策略sp.startReloadIfChangedUnexpectedly();}return sp;}

在该方法中首先看下 getSharedPreferencesCacheLocked 方法如下:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值