SharedPreferences 分析 --- 之一

概述:

SharedPreferences(简称SP)是Android中很常用的数据存储方式,SP采用key-value(键值对)形式, 

主要用于轻量级的数据存储, 尤其适合保存应用的配置参数, 但不建议使用SP 来存储大规模的数据, 可能会降低性能.以下分析都是基于android 6.0

SP采用xml文件格式来保存数据, 该文件所在目录位于/data/data//shared_prefs/

1, 使用

1,获取SharedPreferences对象

SharedPreferences userSettings = getSharedPreferences("settingapp",Context.MODE_PRIVATE);

2,获取Editor对象

SharedPreferences.Editor editor = userSettings.edit();

3, 存放数据

editor.putString("name","AA");
editor.putString("URL","BB.com");

4, 完成提交(异步的)

editor.commit();

5,获取数据

String name = userSettings.getString("name"," default ");
String url = userSettings.getString("URL","default");

6,删除数据

editor.remove("KEY");

7, 清空数据

editor.clear();

2, 获取SharedPreferences对象

获取SharedPreferences对象不止一种方法,例如在Activity.java中,可以调用getPreferences方法获取,但是建议

使用getSharedPreferences方法,其实最后getPreferences方法也是调用getSharedPreferences方法完成。其实是有Context的子类ContextImpl完成的。

ContextImpl的getSharedPreferences方法主要逻辑如下,

1, sSharedPrefs变量为空就创建sSharedPrefs

synchronized (ContextImpl.class) {
  if (sSharedPrefs == null) {
       sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
  }

2, packagePrefs为空就创建,

final String packageName = getPackageName();
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
    packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
    sSharedPrefs.put(packageName, packagePrefs);
}

3, SharedPreferencesImpl为空就创建该对象并返回。

sp = packagePrefs.get(name);
if (sp == null) {
    File prefsFile = getSharedPrefsFile(name);
    sp = new SharedPreferencesImpl(prefsFile, mode);
    packagePrefs.put(name, sp);
    return sp;
}

由此可知, sSharedPrefs 变量保存着一个进程中所有的以包名区分的packagePrefs,定义如下,

private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;

String 是包名,对应的值也是一个ArrayMap,该ArrayMap中保存着相同包名下创建的String对应的SharedPreferencesImpl对象。

因此,返回的是SharedPreferencesImpl对象, SharedPreferences只是一个接口, SharedPreferencesImpl实现了这个接口。

并且在构造SharedPreferencesImpl对象之前,首先调用getSharedPrefsFile方法构造对应的xml文件。

getSharedPrefsFile方法如下,

public File getSharedPrefsFile(String name) {
    return makeFilename(getPreferencesDir(), name + ".xml");
}

getPreferencesDir方法如下,

private File getPreferencesDir() {
  synchronized (mSync) {
     if (mPreferencesDir == null) {
          mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
     }
     return mPreferencesDir;
  }
}

makeFilename方法如下,

if (name.indexOf(File.separatorChar) < 0) {
   return new File(base, name);
}

先从mSharedPrefsPaths查询是否存在相应文件;

如果文件不存在, 则创建新的xml文件; 如果目录也不存在, 则先创建目录创建目录/data/data/package name/shared_prefs/

其中mSharedPrefsPaths用于记录所有的SP文件, 是以文件名为key的Map数据结构.

SharedPreferencesImpl构造方法如下,

mFile = file;
    //创建为.bak为后缀的备份文件
    mBackupFile = makeBackupFile(file);
    mMode = mode;
    mLoaded = false;
    mMap = null;
    startLoadFromDisk();

同名的.bak备份文件用于发生异常时, 可通过备份文件来恢复数据.

startLoadFromDisk方法如下,

new Thread("SharedPreferencesImpl-load") {
   public void run() {
      synchronized (SharedPreferencesImpl.this) {
          loadFromDiskLocked();
      }
  }
}.start();

mLoaded用于标记SP文件已加载到内存. 创建线程调用loadFromDiskLocked方法完成从磁盘加载sp文件的工作.

startLoadFromDisk主要逻辑如下,

1,构造Map对象,以及相关输入流

Map map = null;
StructStat stat = null;
try {
   stat = Os.stat(mFile.getPath());
   if (mFile.canRead()) {
      BufferedInputStream str = null;
      try {
         str = new BufferedInputStream(new FileInputStream(mFile), 16*1024);
map = XmlUtils.readMapXml(str);

2,为相关变量赋值,这些完成之后,才释放线程锁,才可以执行其他方法,

mLoaded = true;//将mLoaded 赋值
if (map != null) {
   mMap = map;
   mStatTimestamp = stat.st_mtime;
   mStatSize = stat.st_size;
} else {
   mMap = new HashMap<String, Object>();
}
notifyAll();

3, edit方法

SharedPreferencesImpl的edit方法如下,

synchronized (this) {
  awaitLoadedLocked();
}
return new EditorImpl();

awaitLoadedLocked方法如下,

if (!mLoaded) {
  BlockGuard.getThreadPolicy().onReadFromDisk();
}
  while (!mLoaded) {
    try {
       wait();//当没有加载完成,则进入等待状态
    } catch (InterruptedException unused) {
 }
}

EditorImpl 是SharedPreferencesImpl的内部类,实现了Editor接口,定义如下,

public final class EditorImpl implements Editor {

4,数据操作

数据操作主要是调用EditorImpl对象的方法,有2个变量,

private final Map<String, Object> mModified = Maps.newHashMap();
private boolean mClear = false;

mModified 主要是缓存数据, mClear 判断是否清零。

例如,常用的putString方法如下,

public Editor putString(String key, @Nullable String value) {
   synchronized (this) {
      mModified.put(key, value);
      return this;
   }
}

其他保存数据方法如下,

public Editor putStringSet(String key, @Nullable Set<String> values) {
 synchronized (this) {
  mModified.put(key, (values == null) ? null : new HashSet<String>(values));
      return this;
  }
}

public Editor putInt(String key, int value) {
   synchronized (this) {
      mModified.put(key, value);
      return this;
  }
}
•••

由此可见,一般只能保存基本的类型的数据和Set 集合。

删除数据的remove方法如下,

public Editor remove(String key) {
  synchronized (this) {
    mModified.put(key, this);
    return this;
  }
}

清除数据的clear方法如下,

public Editor clear() {
  synchronized (this) {
    mClear = true;
    return this;
    }
}
    原文作者:Achillisjack
    原文地址: https://blog.csdn.net/u012439416/article/details/78451800
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞