Android源码之SharedPreferences

0. 前言SharedPreferences可以说是Android中最常用的一种存数据到文件的方式。他的数据是以键值对的方式存储在 ~/data/data/包名/shared_prefs 这个文件夹中的。这个存储框架是非常轻量级的,如果我们需要存一些小数据或者是一个小型的可序列化的Bean实体类的,使用SharedPreferences是最明智的选择。1. 使用方法1.1 获取SharedPreferences在使用SharedPreferences前,我们得先获取到它。由于SharedPref
摘要由CSDN通过智能技术生成

0. 前言

SharedPreferences可以说是Android中最常用的一种存数据到文件的方式。他的数据是以键值对的方式存储在 ~/data/data/包名/shared_prefs 这个文件夹中的。

这个存储框架是非常轻量级的,如果我们需要存一些小数据或者是一个小型的可序列化的Bean实体类的,使用SharedPreferences是最明智的选择。

1. 使用方法

1.1 获取SharedPreferences

在使用SharedPreferences前,我们得先获取到它。

由于SharedPreferences是Android内置的一个框架,所以我们想要获取到它非常的简单,不需要导入任何依赖,直接写代码就行。下面我们就来介绍下获取对象的三个方式:

1.1.1 Context # getSharedPreferences()

首先就是可以说是最常用的方法,通过Context的getSharedPreferences() 方法去获取到SharedPreferences对象。由于是通过Context获取的,所以基本上Android的所有场景我们都可以通过这个方法获取到。

public abstract SharedPreferences getSharedPreferences (String name, 
                int mode)

这个方法接收两个参数,分别是namemode

  • name:name就是我们要存储的SharedPreferences本地文件的名字,这个可以自定义。但是如果使用同样的name的话,永远只能获取到同一个SharedPreferences的对象。
  • mode:mode就是我们要获取的这个SharedPreferences的访问模式,Android给我们提供了挺多的模式的,但是由于其余的模式或多或少存在着安全隐患(因为其他应用也可以直接获取到),所以就全部都弃用了,现在就只有一个MODE_PRIVATE模式。

此外,这个方法是线程安全的。

Mode的可选参数:

  • MODE_PRIVATE:私有模式,该SharedPreferences只会被调用他的APP去使用,其他的APP无法获取到这个SharedPreferences。
  • MODE_WORLD_READABLE:API17被弃用。使用这个模式,所有的APP都可以对这个SharedPreferences进行读操作。所以这个模式被Android官方严厉警告禁止使用(It is strongly discouraged),并推荐使用ContentProviderBroadcastReceiverService
  • MODE_WORLD_WRITEABLE:API17被弃用。和上面类似,这个是可以被所有APP进行写操作。同样也是被严厉警告禁止使用。
  • MODE_MULTI_PROCESS:API23被弃用。使用了这个模式,允许多个进程对同一个SharedPreferences进行操作,但是后来也被启用了,原因是因为在某些Android版本下,这个模式不能可靠的运行,官方建议如果多进程建议使用ContentProvider去操作。在后面我们会说为啥多进程下不可靠。

1.1.2 Activity # getPreferences()

这个方法只能在Activity中或者通过Activity对象去使用。

public SharedPreferences getPreferences (int mode)

这个方法需要传入一个mode参数,这个参数和上面的context#getSharedPreferences()mode参数是一样的。其实这个方法和上面Context的那个方法是一样的,他两都是调用的SharedPreferences getSharedPreferences(String name, int mode)。只不过Context的需要你去指定文件名,而这个方法你不需要手动去指定,而是会自动将当前Activity的类名作为了文件名。

1.1.3 PreferencesManager # getDefaultSharedPreferences()

这个一般用在Android的设置页面上,或者说,我们也只有在构建设置页面的时候才会去使用这个。

public static SharedPreferences getDefaultSharedPreferences (Context context)

他承接一个context参数,并自动将当前应用的报名作为前缀来命名文件。

1.2 存数据

如果需要往SharedPreferences中存储数据的话,我们并不能直接对SharedPreferences对象进行操作,因为SharedPreferences没有提供存储或者修改数据的接口。

如果想要对SharedPreferences存储的数据进行修改,需要通过SharedPreferences.edit()方法去获取到SharedPreferences.Editor对象来进行操作。

获取到Editor对象后,我们就可以调用他的putXXX()方法进行存储了,存储之后一定记得通过apply()commit()方法去将数据提交。

至于commitapply的区别我们后面会说。

 //步骤1:创建一个SharedPreferences对象
 SharedPreferences sharedPreferences= getSharedPreferences("data",Context.MODE_PRIVATE);
 //步骤2: 实例化SharedPreferences.Editor对象
 SharedPreferences.Editor editor = sharedPreferences.edit();
 //步骤3:将获取过来的值放入文件
 editor.putString("name", “Tom”);
 editor.putInt("age", 28);
 editor.putBoolean("marrid",false);
 //步骤4:提交               
 editor.commit();
 
// 删除指定数据
 editor.remove("name");
 editor.commit();
 
// 清空数据
 editor.clear();
 editor.commit();

1.3 取数据

取值就很简单了,构建出SharedPreferences的对象后,就直接调用SharedPreferences的getXXX()方法就行。

SharedPreferences sharedPreferences = getSharedPreferences("data", Context .MODE_PRIVATE);
String userId = sharedPreferences.getString("name", "");

2. 源码分析

2.1 获取SharedPreferences实例

我们上面说到,获取SharedPreferences实例最常用的方法就是Context#getSharedPreferences()。那我们就从这个方法入手,看到底是怎么获取到SharedPreferences实例的。

我们先看下这个方法的实现:

public class ContextWrapper extends Context {
   
    @UnsupportedAppUsage
    Context mBase;
    
    // ...
    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
   
        return mBase.getSharedPreferences(name, mode);
    }
}

可以看到他又调用了Context的getSharedPreferences()方法:

public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);

然后我们就会惊喜的发现,这是一个抽象方法。我开始还想去找一个ContextWrapper的构造的地方,看看mBase传入的是啥,后来找了一圈没找到,直接上网搜索,立马得到答案:ContextImpl,这个可以说是Context在Android中的唯一实现类,所有的操作又得经过这个类。那么我们就来看下这个类中的getSharedPreferences()方法的实现:

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
   
    // At least one application in the world actually passes in a null
    // name.  This happened to work because when we generated the file name
    // we would stringify it to "null.xml".  Nice. 
            // ps:这个nice很精髓😂
    if (mPackageInfo.getApplicationInfo().targetSdkVersion <
            Build.VERSION_CODES.KITKAT) {
   
        if (name == null) {
   
            name = "null";
        }
    }

    File file;
    // 加了一个类锁,保证同步
    synchronized (ContextImpl.class) {
   
        // mSharedPrefsPaths是一个保存了name和file对应关系的ArrayMap
        if (mSharedPrefsPaths == null) {
   
            mSharedPrefsPaths = new ArrayMap<>();
        }
        // 根据name从里面找有没有缓存的file
        file = mSharedPrefsPaths.get(name);
        // 如果没有,那就调用getSharedPreferencesPath去找
        if (file == null) {
   
            // ->>> 重点1. getSharedPreferencesPath(name)
            file = getSharedPreferencesPath(name);
            // 并保存到mSharedPrefsPaths
            mSharedPrefsPaths.put(name, file);
        }
    }
    // 获取到file后,再调用getSharedPreferences
    return getSharedPreferences(file, mode);
}

/**
 * 重点1. ContextImpl # getSharedPreferencesPath(String name)
 *   根据PreferencesDir和name.xml去创建了这个文件
 */
@Override
public File getSharedPreferencesPath(String name) {
   
    return makeFilename(getPreferencesDir(), name + ".xml");
}

那我们在看下getSharedPreferences(File file, int mode)的实现:

@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
   
    // SharedPreferences唯一实现类SharedPreferencesImpl的实例
    SharedPreferencesImpl sp;
    // 同样的加类锁
    synchronized (ContextImpl.class) {
   
        // 构造了一个File-SharedPreferencesImpl对应关系的ArrayMap
        // 调用getSharedPreferencesCacheLocked方法区获取cahce
        // ->>> 重点1
        final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
        // 从file-SharedPreferencesImpl键值对中根据当前file去过去SharedPreferencesImpl实例
        sp = cache.get(file);
        // 如果没有,那就需要新建一个
        if (sp == null) {
   
            // 检查mode,如果是MODE_WORLD_WRITEABLE或者MODE_MULTI_PROCESS则直接抛异常
            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);
            // 将对象和file的键值对存入cache中
            cache.put(file, sp);
            return sp;
        <
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值