当前位置:   article > 正文

Sharepreferences 使用和实现分析

sharepreferences

是什么

Android平台用于存储轻量级数据的存储方式,以键值对(key-value)的方式来进行存储,本质上是一个xml 文件。

怎么用

1、通过Context,获取SharedPreferences对象;
2、创建SharedPreferences.Editor对象;
3、写入数据并执行commit操作;

Context context;
SharedPreferences sharedPreferences = context.getSharedPreferences("sptest",MODE_PRIVATE);
        SharedPreferences.Editor editor=sharedPreferences.edit();
        editor.putString("name","名字");
        editor.commit();
  • 1
  • 2
  • 3
  • 4
  • 5

执行上面的操作之后,就会在 data/data/你的应用名称/shared_prefs 文件夹下创建 sptest.xml 文件,并写入对应的数据。
getSharedPreferences传入的参数,为 MODE_PRIVATE,也就是默认的模式。Android支持的模式一共有以下四种操作模式。四种操作模式分别为:

1. MODE_APPEND: 追加方式存储
2. MODE_PRIVATE: 私有方式存储,其他应用无法访问
3. MODE_WORLD_READABLE: 表示当前文件可以被其他应用读取
4. MODE_WORLD_WRITEABLE: 表示当前文件可以被其他应用写入
  • 1
  • 2
  • 3
  • 4

现在只推荐使用MODE_PRIVATE这种模式,其他的几种模式,已经被官方弃用了,不再推荐使用。甚至在N版本以上,会直接抛异常。
在这里插入图片描述

1、通过上面获取到的SharedPreferences,执行器getString 方法即可;

String name = sharedPreferences.getString("name", "默认值");
  • 1

实现原理

SharedPreferences是一个接口,对应的实现者是,SharedPreferencesImpl。在我们创建SharedPreferences的时候,需要传入对应的文件名称,然后将对应文件的数据读取到内存中,这个操作是异步的,在其内部新创建了一条名称为SharedPreferencesImpl-load的Thread进行读取的操作,

    private void startLoadFromDisk() {
        synchronized (this) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                synchronized (SharedPreferencesImpl.this) {
                    loadFromDiskLocked();
                }
            }
        }.start();
    }    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

当我们进行数据新增的时候,并不会立即进行数据落地,会将所有的操作,都先放进内存里面的Map,

        private final Map<String, Object> mModified = Maps.newHashMap();
        public Editor putString(String key, String value) {
            synchronized (this) {
                mModified.put(key, value);
                return this;
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

当需要进行数据落地的时候,需要调用commit或者apply方法。

        public void apply() {
        ...
            final Runnable awaitCommit = new Runnable() {
                    public void run() {
                        try {
                            mcr.writtenToDiskLatch.await();
                        } catch (InterruptedException ignored) {
                        }
                    }
                };
            Runnable postWriteRunnable = new Runnable() {
                    public void run() {
                        awaitCommit.run();
                        QueuedWork.remove(awaitCommit);
                    }
                };
            SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
        }

    private void writeToFile(MemoryCommitResult mcr) {
        if (mFile.exists()) {
            if (!mcr.changesMade) {
                mcr.setDiskWriteResult(true);
                return;
            }
            if (!mBackupFile.exists()) {
                if (!mFile.renameTo(mBackupFile)) {
                    Log.e(TAG, "Couldn't rename file " + mFile
                          + " to backup file " + mBackupFile);
                    mcr.setDiskWriteResult(false);
                    return;
                }
            } else {
                mFile.delete();
            }
        }
        try {
            FileOutputStream str = createFileOutputStream(mFile);
            if (str == null) {
                mcr.setDiskWriteResult(false);
                return;
            }
            XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);
            FileUtils.sync(str);
            str.close();
            ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
            try {
                final StructStat stat = Libcore.os.stat(mFile.getPath());
                synchronized (this) {
                    mStatTimestamp = stat.st_mtime;
                    mStatSize = stat.st_size;
                }
            } catch (ErrnoException e) {}
            mBackupFile.delete();
            mcr.setDiskWriteResult(true);
            return;
        } catch (XmlPullParserException e) {
            Log.w(TAG, "writeToFile: Got exception:", e);
        } catch (IOException e) {
            Log.w(TAG, "writeToFile: Got exception:", e);
        }
        if (mFile.exists()) {
            if (!mFile.delete()) {
                Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
            }
        }
        mcr.setDiskWriteResult(false);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

这两者的差别是,commit 是同步操作,而apply 是异步实现的。在将writeToFile方法中,首先将当前文件重命名为备份文件,然后从当前文件中获取文件输出流,并将MemoryCommitResult中保存的备份数据,写入到文件输出流中。如果写入成功,则删除备份文件,返回true。如果写入失败,则删除当前文件,在下一次从备份文件中恢复过来。

QA

  • SP支持多线程吗?SP支持多进程吗?

两种都不支持,如果非要使用,会存在数据丢失的情况。

  • SP支持被其他APP 访问吗?

支持,但要访问的应用的Preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限,否则都是访问不了的。

  • 自己创建一个同名同权限的文件放在对应的目录下,SP支持进行读写吗?

可以,但要求你的手机是root的机子,按照应用创建的方式,赋予对应的用户和用户组,还有响应的权限即可。如果遇到不成功的情况,可能是权限没有给到位,可以参考:http://bbs.elecfans.com/jishu_1654998_1_1.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/308769
推荐阅读
相关标签
  

闽ICP备14008679号