美文网首页
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