安卓面试题(15/30)SharedPreferences

牛客高级系列专栏:

安卓(安卓系统开发也要掌握)


嵌入式


本人是2020年毕业于广东工业大学研究生:许乔丹,有国内大厂CVTE和世界500强企业安卓开发经验,该专栏整理本人对常见安卓高频开发面试题的理解;

网上安卓资料千千万,笔者将继续维护专栏,一杯奶茶价格不止提供答案解析,承诺提供专栏内容免费技术答疑,直接咨询即可。助您提高安卓面试准备效率,为您面试保驾护航!

正文开始⬇

永久化存储是日常开发中常用的知识点,除了用数据库存储,很多开发者会使用SharedPreferences进行永久化存储,因此SharedPreferences也是一个考点,面试官可能会问:

  1. SharedPreference是线程安全的吗?⭐⭐⭐
  2. SharedPreference的apply和commit的区别?commit 一定会在主线程操作嘛?⭐⭐⭐⭐⭐
  3. SharedPreferences 是如何初始化的,它会阻塞主线程吗?⭐⭐⭐
  4. 每次获取 SP 对象真的会很慢吗?⭐⭐⭐
  5. 在使用时需要注意哪些问题,以及有什么优化点呢?⭐⭐⭐⭐

看完以下的解析,一定可以让面试官眼前一亮。

目录

  • 1、SharedPreferences 是什么?怎么用?
  • 2、源码分析
    • 2.1 获取SharedPreferencesImpl实例
    • 2.2 SharedPreferencesImpl实例分析
    • 2.3 commit和apply
  • 3、总结

1、SharedPreferences 是什么?怎么用?

从文章开头就知道SharedPreferences是Android用于永久性存储的一种方式,顺便复习下Android的永久性存储方式有哪些:

  • File
  • SharedPreferences
  • SQlite
  • 网络
  • ContentProvider

SharedPreferences是基于key-value 键值对生成的一个xml文件,保存在/data/data/packageName/shared_prefs目录下,适合保存少量数据,且数据格式相对简单。

SharedPreferences sharedPreferences = context.getSharedPreferences(“xurui”, Context.MODE_PRIVATE); //1
SharedPreferences.Editor editor = sharedPreferences.edit(); //2
editor.putString("key", "value"); //3
editor.apply(); //4

//使用
String value = sharedPreferences.getString("key", "defaultValue"); //5

SharedPreferences本身是一个接口,可以通过getSharedPreferences()获取实例,原型是:

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

其中name是最终生成的xml文件的文件名,mode代表不同的存储方式,查看源码有:

@IntDef(flag = true, prefix = { "MODE_" }, value = {
        MODE_PRIVATE,
        MODE_WORLD_READABLE,
        MODE_WORLD_WRITEABLE,
        MODE_MULTI_PROCESS,
})
@Retention(RetentionPolicy.SOURCE)
public @interface PreferencesMode {}

mode分成以下5种类型:

  • MODE_PRIVATE:表示该 SharedPreferences数据只能被本应用读写,或者有相同用户ID的应用读写;
  • MODE_WORLD_READABLE:全局读,允许所有其他应用程序对创建的文件具有读访问权限,该模式官方强烈建议不使用,因为会造成安全问题;
  • MODE_WORLD_WRITEABLE:全局写,允许所有其他应用程序对创建的文件有写访问权限,官方同样建议不使用;
  • MODE_MULTI_PROCESS:可以实现多进程访问SharedPreferences数据的问题,但是这种方式的多进程共享数据可能会出现数据不一致的问题,也不可靠,现在也不使用了;
  • MODE_APPED:该模式不在规定的PreferencesMode模式里,但也可以使用,如果文件存在且对应的key也存在,则可以在对应的value数值追加新的内容,不同于MODE_PRIVATE,MODE_PRIVATE会把旧的内容覆盖掉。

接着,获取到的SharedPreferences实例本身仅支持获取数据,如果需要修改或者储存,需要通过SharedPreferences.Editor来实现,同样Editor也是接口,可以通过sharedPreferences.edit()获取实例,并通过putString修改内容,最后执行apply()即可(apply()下面会分析)。如此我们就完成了一对key-value 键值对的存放,到/data/data/packageName/shared_prefs下就可以找到一个叫“xurui.xml"的文件,里面就存放着刚添加的键值对。

2、源码分析

2.1 获取SharedPreferencesImpl实例

源码分析的思路,按上一节使用案例来分析: 首先看注释1:context.getSharedPreferences(“xurui”, Context.MODE_PRIVATE),查看源码发现该函数在/frameworks/base/core/java/android/app/ContextImpl.java

 @Override
    public SharedPreferences getSharedPreferences(String name, int mode) { //6
        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
                Build.VERSION_CODES.KITKAT) {
            if (name == null) {
                name = "null";
            }
        }
        File file;
        synchronized (ContextImpl.class) {
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>(); //7
            }
            file = mSharedPrefsPaths.get(name); //8
            if (file == null) { //9
                file = getSharedPreferencesPath(name);
                mSharedPrefsPaths.put(name, file);
            }
        }
        return getSharedPreferences(file, mode); //10
    }

    @Override
    public SharedPreferences getSharedPreferences(File file, int mode) {
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked(); //11
            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");
                    }
                }
                sp = new SharedPreferencesImpl(file, mode); //12
                cache.put(file, sp);
                return sp;
            }
        }
        if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
            getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
            sp.startReloadIfChangedUnexpectedly();
        }
        return sp;
    }

    @GuardedBy("ContextImpl.class")
    private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
        if (sSharedPrefsCache == null) {
            sSharedPrefsCache = new ArrayMap<>(); //13
        }

        final String packageName = getPackageName();
        ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName); //14
        if (packagePrefs == null) {
            packagePrefs = new ArrayMap<>();
            sSharedPrefsCache.put(packageName, packagePrefs);
        }
        return packagePrefs;
    }

上面总共有3个函数,环环相扣,context.getSharedPreferences(“xurui”, Context.MODE_PRIVATE)对应注释6.如上所述,这里“xurui”是指一个xml文件的名字,注释7的mSharedPrefsPaths是一个Map数据,其中key对应文件名称,value则是对应的xml文件。如果输入的文件名存在,则获取出对应文件,如注释8.如果文件不存在则创建并保存,对应注释9.最后获取到的文件传入注释10,也就是上面的第2个函数。

进入函数2,看看注释11,这一行需要重点理,其中:

  • File:xml文件名;
  • SharedPreferencesImpl:xml文件名在内存对应的实例,源码并非简单的对xml文件进行读写,而是每个文件会创建专门的实例来执行各种操作和判断;

所以我们看看getSharedPreferencesCacheLocked()做了什么,这也就是上面的第3个函数。首先会创建sSharedPrefsCache,这也是一个Map,其key是包名,value是packagePrefs,对应注释14,其中:

  • sSharedPrefsCache:定义如下,也是在ContextImpl.java中定义,因为一个进程只有一个ContextImpl对象,一个ContextImp只定义一个sSharedPrefsCache,所以一个进程也就只有一个sSharedPrefsCache;
private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
  • packageName:一个进程只有一个sSharedPrefsCache,但是可能有多个应用,因此需要用包名做区分;
  • SharedPreferencesImpl:xml文件名在内存对应的实例,源码并非简单的对xml文件进行读写,而是每个文件会创建专门的实例来执行各种操作和判断;
  • packagePrefs: SharedPreferences 文件与 SharedPreferences 实例对象之间的映射关系;

如果getSharedPreferencesCacheLocked()中找不到输入文件名对应的SharedPreferencesImpl实例,则在注释12根据文件名和模式,新创建一个实例并返回。

综上,总结一下传入文件名获取SharedPreferencesImpl实例的过程:

首先sSharedPrefsCache会保存从磁盘加载到内

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Android高频面试题全解析 文章被收录于专栏

#提供免费售后答疑!!花一杯奶茶的钱获得安卓面试答疑服务,稳赚不赔# Android发展已经很多年,安卓资料网上千千万,本专栏免费提供专栏内容技术答疑!!私聊当天必回。在阅读过程或者其他安卓学习过程有疑问,都非常欢迎私聊交流。

全部评论
这全是framework的
点赞 回复 分享
发布于 2022-12-25 23:05 上海

相关推荐

微风不断:兄弟,你把四旋翼都做出来了那个挺难的吧
点赞 评论 收藏
分享
11-15 18:39
已编辑
西安交通大学 Java
全村最靓的仔仔:卧槽,佬啥bg呢,本也是西交么
点赞 评论 收藏
分享
12 13 评论
分享
牛客网
牛客企业服务