美文网首页
SharePreferences的源码流程

SharePreferences的源码流程

作者: s1991721 | 来源:发表于2018-05-22 21:25 被阅读0次

SharePreferences是Android中存储数据的常用功能,

SharePreferences

使用的时候一般通过Context的public SharedPreferences getSharedPreferences(String name, int mode)获得,当然也可以自己指定文件public SharedPreferences getSharedPreferences(File file, int mode)获得。

    public SharedPreferences getSharedPreferences(String name, int mode) {
        //4.4以下将null转成字符串,即使是4.4以上name为空
        //mSharedPrefsPaths.get(name)也不会崩溃,有专门的null键存储数据,类似HashMap
        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
                Build.VERSION_CODES.KITKAT) {
            if (name == null) {
                name = "null";
            }
        }

        File file;
        //锁,SharePreferences源码操作中很多地方都在用
        synchronized (ContextImpl.class) {
            if (mSharedPrefsPaths == null) {
                mSharedPrefsPaths = new ArrayMap<>();//name:file键值对缓存,方便下次读取
            }
            file = mSharedPrefsPaths.get(name);
            if (file == null) {
                file = getSharedPreferencesPath(name);//根据路径取或创建file
                mSharedPrefsPaths.put(name, file);//并缓存
            }
        }
        return getSharedPreferences(file, mode);//根据文件和mode找SharedPreferences
    }
    public SharedPreferences getSharedPreferences(File file, int mode) {
        checkMode(mode);//7.0以上不再支持MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE并抛SecurityException
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {//对缓存操作加锁
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();//package:(file:sharedpreference)键值对,缓存package下的ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>>
            sp = cache.get(file);
            if (sp == null) {
                sp = new SharedPreferencesImpl(file, mode);
                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;
    }
SharedPreferencesImpl

SharedPreferencesImpl中有两个内部类,我们能接触到的是EditorImpl它实现了SharedPreferences.Editor

SharedPreferences

做存储操作的时候第一步edit(),代码可以看出每次产生一个新EditorImpl对象,这也是使用不当,存储数据丢失的原因之一

    public Editor edit() {
        synchronized (this) {
            awaitLoadedLocked();
        }

        return new EditorImpl();//每次返回一个新的Editor,慎
    }

EditorImpl所有的存操作,都暂时存在Map里,clear只是一个标志位,数据实际的存储操作在apply、commit中执行。

    public final class EditorImpl implements Editor {
        private final Map<String, Object> mModified = Maps.newHashMap();//临时存储
        private boolean mClear = false;//清空标志位

        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;
            }
        }
        public Editor putLong(String key, long value) {
            synchronized (this) {
                mModified.put(key, value);
                return this;
            }
        }
        public Editor putFloat(String key, float value) {
            synchronized (this) {
                mModified.put(key, value);
                return this;
            }
        }
        public Editor putBoolean(String key, boolean value) {
            synchronized (this) {
                mModified.put(key, value);
                return this;
            }
        }

        public Editor remove(String key) {//这里很巧妙,并没有移除对象,为什么呢!看看下面的commitToMemory
            synchronized (this) {
                mModified.put(key, this);
                return this;
            }
        }

        public Editor clear() {//置标志位,实际的操作在后执行
            synchronized (this) {
                mClear = true;
                return this;
            }
        }

        ····
        省略部分代码
        ····
    }

先看下commit()的即时操作

        public boolean commit() {
            MemoryCommitResult mcr = commitToMemory();//MemoryCommitResult可理解为builder存储信息用,为避免思路跳跃放到后面
            SharedPreferencesImpl.this.enqueueDiskWrite(
                mcr, null /* sync write on this thread okay */);//开始存储,null表示后序的Runnable操作
            try {
                mcr.writtenToDiskLatch.await();//等待结果countDown
//MemoryCommitResult里设置的CountDownLatch(1),当存储结束后调用setDiskWriteResult方法,在此内部countDown
            } catch (InterruptedException e) {
                return false;
            }
            notifyListeners(mcr);//回调变化
            return mcr.writeToDiskResult;//返回操作结果
        }

到此看不出即时性再往里走

    private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                  final Runnable postWriteRunnable) {
        //操作用的Runnable
        final Runnable writeToDiskRunnable = new Runnable() {
                public void run() {
                    synchronized (mWritingToDiskLock) {
                        writeToFile(mcr);
                    }
                    synchronized (SharedPreferencesImpl.this) {
                        mDiskWritesInFlight--;
                    }
                    if (postWriteRunnable != null) {
                        postWriteRunnable.run();
                    }
                }
            };

        final boolean isFromSyncCommit = (postWriteRunnable == null);//无后序操作判定为即时操作

        if (isFromSyncCommit) {//是否同步提交的判断
            boolean wasEmpty = false;
            synchronized (SharedPreferencesImpl.this) {
                wasEmpty = mDiskWritesInFlight == 1;
            }
            if (wasEmpty) {
                writeToDiskRunnable.run();//主线程内完成
                return;
            }
        }

        QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);//线程池操作
    }

由于commit()中

SharedPreferencesImpl.this.enqueueDiskWrite(
                mcr, null);

第二个参数为null,所以为同步操作。

看看异步提交apply()

        public void apply() {
            final MemoryCommitResult mcr = commitToMemory();
            final Runnable awaitCommit = new Runnable() {//等待结果的Runnable
                    public void run() {
                        try {
                            mcr.writtenToDiskLatch.await();
                        } catch (InterruptedException ignored) {
                        }
                    }
                };

            QueuedWork.add(awaitCommit);

            Runnable postWriteRunnable = new Runnable() {//后序操作的Runnable,实际就是awaitCommit的操作,多了个队列操作
                    public void run() {
                        awaitCommit.run();
                        QueuedWork.remove(awaitCommit);
                    }
                };

            SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);//异步的原因

            // 效果实际产生前就回调
            // Okay to notify the listeners before it's hit disk
            // because the listeners should always get the same
            // SharedPreferences instance back, which has the
            // changes reflected in memory.
            notifyListeners(mcr);
        }

实际的写文件

    private void writeToFile(MemoryCommitResult mcr) {
        // Rename the current file so it may be used as a backup during the next read将当前文件删除,重新写入文件
        if (mFile.exists()) {
            if (!mcr.changesMade) {//文件存在且没有修改,不执行操作
                // If the file already exists, but no changes were
                // made to the underlying map, it's wasteful to
                // re-write the file.  Return as if we wrote it
                // out.
                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();
            }
        }

        // Attempt to write the file, delete the backup and return true as atomically as
        // possible.  If any exception occurs, delete the new file; next time we will restore
        // from the backup.
        try {
            FileOutputStream str = createFileOutputStream(mFile);//文件输出流
            if (str == null) {//获取输出流失败返回
                mcr.setDiskWriteResult(false);
                return;
            }
            XmlUtils.writeMapXml(mcr.mapToWriteToDisk, str);//将map写到文件
            FileUtils.sync(str);//同步流
            str.close();//关闭流
            ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);//给文件设置权限
            try {
                final StructStat stat = Os.stat(mFile.getPath());
                synchronized (this) {//文件的时间戳大小信息,用于多进程操作的判断
                    mStatTimestamp = stat.st_mtime;
                    mStatSize = stat.st_size;
                }
            } catch (ErrnoException e) {
                // Do nothing
            }
            // Writing was successful, delete the backup file if there is one.
            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);
        }
        // Clean up an unsuccessfully written file
        if (mFile.exists()) {//当意外发生删除文件
            if (!mFile.delete()) {
                Log.e(TAG, "Couldn't clean up partially-written file " + mFile);
            }
        }
        mcr.setDiskWriteResult(false);//保存失败
    }

保存流程完后,下次使用时

    SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        mBackupFile = makeBackupFile(file);//加载备份文件否则新建
        mMode = mode;
        mLoaded = false;
        mMap = null;
        startLoadFromDisk();
    }

加载数据

    private void startLoadFromDisk() {
        synchronized (this) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {//在子线程加载数据
            public void run() {
                loadFromDisk();
            }
        }.start();
    }

    private void loadFromDisk() {
        synchronized (SharedPreferencesImpl.this) {
            if (mLoaded) {
                return;
            }
            if (mBackupFile.exists()) {//发现有备份,将备份认为文件,因为之前的存储完成后已经将备份文件删除了,有备份文件是因为之前存储失败,数据在备份中,加载文件没有备份有意义
                mFile.delete();
                mBackupFile.renameTo(mFile);
            }
        }

        // Debugging文件权限检查
        if (mFile.exists() && !mFile.canRead()) {
            Log.w(TAG, "Attempt to read preferences file " + mFile + " without permission");
        }

        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);//读取出map
                } catch (XmlPullParserException | IOException e) {
                    Log.w(TAG, "getSharedPreferences", e);
                } finally {
                    IoUtils.closeQuietly(str);
                }
            }
        } catch (ErrnoException e) {
            /* ignore */
        }

        synchronized (SharedPreferencesImpl.this) {//将mMap = map数据加载成功
            mLoaded = true;
            if (map != null) {
                mMap = map;
                mStatTimestamp = stat.st_mtime;
                mStatSize = stat.st_size;
            } else {
                mMap = new HashMap<>();
            }
            notifyAll();
        }
    }

上面跳过了commitToMemory,它的作用就是构建MemoryCommitResult(可理解为SharePreferences的builder)

        private MemoryCommitResult commitToMemory() {
            MemoryCommitResult mcr = new MemoryCommitResult();
            synchronized (SharedPreferencesImpl.this) {
                // We optimistically don't make a deep copy until
                // a memory commit comes in when we're already
                // writing to disk.
                if (mDiskWritesInFlight > 0) {//第一次写disk
                    // We can't modify our mMap as a currently
                    // in-flight write owns it.  Clone it before
                    // modifying it.
                    // noinspection unchecked
                    mMap = new HashMap<String, Object>(mMap);//将之前文件的内容copy
                }
                mcr.mapToWriteToDisk = mMap;//将要写入disk的map
                mDiskWritesInFlight++;//计数

                boolean hasListeners = mListeners.size() > 0;//监听器数量
                if (hasListeners) {
                    mcr.keysModified = new ArrayList<String>();//监听的key
                    mcr.listeners =
                            new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());//此map的key:value=listener:object,所以此处用keyset就是Listener集合
                }

                synchronized (this) {
                    if (mClear) {//清空意味着清空以前数据,如果一个Editor有清空又有添加操作,则清空以前SharePreferences中的数据,保存此次的添加数据
                        if (!mMap.isEmpty()) {
                            mcr.changesMade = true;
                            mMap.clear();
                        }
                        mClear = false;
                    }

                    for (Map.Entry<String, Object> e : mModified.entrySet()) {//循环临时存储中的数据,加入将要写入disk的map中
                        String k = e.getKey();
                        Object v = e.getValue();
                        // "this" is the magic value for a removal mutation. In addition,
                        // setting a value to "null" for a given key is specified to be
                        // equivalent to calling remove on that key.
                        if (v == this || v == null) {//remove操作的this在这里,设值为null也会执行移除操作
                            if (!mMap.containsKey(k)) {
                                continue;
                            }
                            mMap.remove(k);
                        } else {
                            if (mMap.containsKey(k)) {
                                Object existingValue = mMap.get(k);
                                if (existingValue != null && existingValue.equals(v)) {
                                    continue;
                                }
                            }
                            mMap.put(k, v);
                        }

                        mcr.changesMade = true;//标志位表示需要写入disk
                        if (hasListeners) {//将监听的关键字记录
                            mcr.keysModified.add(k);
                        }
                    }

                    mModified.clear();//临时变量清空
                }
            }
            return mcr;
        }

还有一个回调功能

        private void notifyListeners(final MemoryCommitResult mcr) {
            if (mcr.listeners == null || mcr.keysModified == null ||
                mcr.keysModified.size() == 0) {
                return;
            }
            if (Looper.myLooper() == Looper.getMainLooper()) {//本身线程在主线程
                for (int i = mcr.keysModified.size() - 1; i >= 0; i--) {
                    final String key = mcr.keysModified.get(i);
                    for (OnSharedPreferenceChangeListener listener : mcr.listeners) {
                        if (listener != null) {
                            listener.onSharedPreferenceChanged(SharedPreferencesImpl.this, key);
                        }
                    }
                }
            } else {//回到主线程,方法可以借用
                // Run this function on the main thread.
                ActivityThread.sMainThreadHandler.post(new Runnable() {
                        public void run() {
                            notifyListeners(mcr);
                        }
                    });
            }
        }

相关文章

网友评论

      本文标题:SharePreferences的源码流程

      本文链接:https://www.haomeiwen.com/subject/cptkjftx.html