美文网首页
源码学习->18SharedPreferences

源码学习->18SharedPreferences

作者: 冉桓彬 | 来源:发表于2017-05-03 13:10 被阅读16次

    一、SharedPreferences构建:

    1.1 ContextImpl.getSharedPreferences:
    
    private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
    
    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            if (sSharedPrefs == null) {
                sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
            }
    
            final String packageName = getPackageName();
            ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
            if (packagePrefs == null) {
                packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
                sSharedPrefs.put(packageName, packagePrefs);
            }
            /**
             * api19以下支持name = null;
             */
            if (mPackageInfo.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                if (name == null) {
                    name = "null";
                }
            }
    
            sp = packagePrefs.get(name);
            if (sp == null) {
                /**
                 * 1. 一个name对应一个SharedPreferencesImpl;
                 * 2. 将File缓存在SharedPreferencesImpl中;
                 */
                File prefsFile = getSharedPrefsFile(name);     模块<1.2>
                sp = new SharedPreferencesImpl(prefsFile, mode);     模块<1.3>
                packagePrefs.put(name, sp);
                return sp;
            }
        }
        return sp;
    }
    
    1.2 ContextImpl.getSharedPrefsFile:
    /**
     * 构建对应的File文件;
     */
    @Override
    public File getSharedPrefsFile(String name) {
        return makeFilename(getPreferencesDir(), name + ".xml");
    }
    
    private File makeFilename(File base, String name) {
        if (name.indexOf(File.separatorChar) < 0) {
            return new File(base, name);
        }
    }
    
    1.3 SharedPreferencesImpl构造函数:
    SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        mBackupFile = makeBackupFile(file);
        mMode = mode;
        mLoaded = false;
        mMap = null;
        /**
         * 1. 结合模块<1.4>可知, 在使用Sp时, 需要先根据name将指定目录下的<name>文件中的内容全部
         *    加载进内存中, 然后在进行读写操作;
         * 2. 数据量越大, 加载就越耗时, 所以这也就是为什么一直强调尽量不要在一个sp中存储大量数据;
         */
        startLoadFromDisk();    模块<1.4>
    }
    
    1.4 SharedPreferencesImpl.startLoadFromDisk:
    private void startLoadFromDisk() {
        synchronized (this) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {
            public void run() {
                synchronized (SharedPreferencesImpl.this) {
                    /**
                     * File中的数据加载进Map是在子线程中进行, 与putXXX操作如何进行同步?  模块<二>
                     */
                    loadFromDiskLocked();
                }
            }
        }.start();
    }
    
    private void loadFromDiskLocked() {
        if (mLoaded) {
            return;
        }
        if (mBackupFile.exists()) {
            mFile.delete();
            mBackupFile.renameTo(mFile);
        }
        Map map = null;
        StructStat stat = null;
        stat = Os.stat(mFile.getPath());
        if (mFile.canRead()) {
            /**
             * 数据量越大, 这一块就越耗时, 进行get/edit操作时如果加载没完成会处于阻塞状态;
             */
            BufferedInputStream str = null;
            str = new BufferedInputStream(new FileInputStream(mFile), 16*1024);
            map = XmlUtils.readMapXml(str);
        }
        mLoaded = true;
        if (map != null) {
            mMap = map;
            mStatTimestamp = stat.st_mtime;
            mStatSize = stat.st_size;
        } else {
            mMap = new HashMap<String, Object>();
        }
        notifyAll();
    }
    

    二、SharedPreferencesImpl.edit:

    public Editor edit() {
        /**
         * 这里的插入操作与初始化Sp时磁盘数据加载到内存共用同一把锁, 如果初始化时的数据加载
         * 耗时过长, 这里就会被一直阻塞;
         */
        synchronized (this) {
            awaitLoadedLocked();
        }
        return new EditorImpl();
    }
    
    private void awaitLoadedLocked() {
        while (!mLoaded) {
            wait();
        }
    }
    

    三、EditorImpl.putXXX:

    public Editor putInt(String key, int value) {
        synchronized (this) {
            /**
             * 对于指定name的Sp, 由于sp与EditorImpl都是单例, 在结合这里的锁对象, 所以Sp是线程安全的;
             */
            mModified.put(key, value);
            return this;
        }
    }
    

    四、EditorImpl.commit:

    4.1 EditorImpl.commit:
    public boolean commit() {
        /**
         * 将数据从缓存集合mModified读取到mMap中, 然后将mMap赋值给MCR;
         */
        MemoryCommitResult mcr = commitToMemory();     模块<4.2>
        /**
         * 将数据从mcr.mapToWriteToDisk中写入到磁盘中;
         */
        SharedPreferencesImpl.this.enqueueDiskWrite(mcr, null);     模块<4.3>
        try {
            mcr.writtenToDiskLatch.await();   
        } catch (InterruptedException e) {
            return false;
        }
        notifyListeners(mcr);
        return mcr.writeToDiskResult;
    }
    
    4.2 EditorImpl.commitToMemory:
    private MemoryCommitResult commitToMemory() {
        MemoryCommitResult mcr = new MemoryCommitResult();
        synchronized (SharedPreferencesImpl.this) {
            if (mDiskWritesInFlight > 0) {
                mMap = new HashMap<String, Object>(mMap);
            }
            mcr.mapToWriteToDisk = mMap;
            mDiskWritesInFlight++;
            boolean hasListeners = mListeners.size() > 0;
            if (hasListeners) {
                mcr.keysModified = new ArrayList<String>();
                mcr.listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
            }
            synchronized (this) {
                for (Map.Entry<String, Object> e : mModified.entrySet()) {
                    String k = e.getKey();
                    Object v = e.getValue();
                    ...
                    mMap.put(k, v);
                    mcr.changesMade = true;
                    if (hasListeners) {
                        mcr.keysModified.add(k);
                    }
                }
                mModified.clear();
            }
        }
        return mcr;
    }
    
    • 简而言之, EditorImpl.putXXX方法将数据缓存进mModified中, 然后调用commit将mModified数据读取到mMap中, 然后赋值给MemoryCommitResult.mapToWriteToDisk, 同时情况mModified;
    4.3 SharedPreferencesImpl.enqueueDiskWrite:
    private void enqueueDiskWrite(final MemoryCommitResult mcr, final Runnable postWriteRunnable) {
        final Runnable writeToDiskRunnable = new Runnable() {
            public void run() {
                // 全局共用一把锁, 写入磁盘操作是线程同步的;
                synchronized (mWritingToDiskLock) {
                    writeToFile(mcr);       模块<4.4>
                }
                synchronized (SharedPreferencesImpl.this) {
                    mDiskWritesInFlight--;
                }
                if (postWriteRunnable != null) {
                    postWriteRunnable.run();
                }
            }
        };
        /**
         * commit方式 ---> isFromSyncCommit = true;
         * apply方式 ---> isFromSyncCommit = false;
         */
        final boolean isFromSyncCommit = (postWriteRunnable == null);
    
        if (isFromSyncCommit) {
            boolean wasEmpty = false;
            synchronized (SharedPreferencesImpl.this) {
                /**
                 * 1. 结合模块<4.2>可知, mDiskWritesInFlight默认为0, 调用一次commit, 触发一次
                 *    mDiskWritesInFlight++ = 1操作, 然后在writeToDiskRunnable.run中又重置
                 *    mDiskWritesInFlight = 0操作;
                 * 2. 所以调用一次commit时, mDiskWritesInFlight == 1, empty = true;
                 */
                wasEmpty = mDiskWritesInFlight == 1;
            }
            if (wasEmpty) {
                /**
                 * 调用commit时会跳转到这里, 触发writeToDiskRunnable.run;
                 */
                writeToDiskRunnable.run();
                return;
            }
        }
        /**
         * 如果是apply方式, 则会执行到这里, 可知, writeToDiskRunnable被运行在子线程中, 然后
         * 触发writeToFile在子线程中执行;
         */
        QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);    模块<5.2>
    }
    
    4.4 SharedPreferencesImpl.writeToFile:
    private void writeToFile(MemoryCommitResult mcr) {
        if (mFile.exists()) {
            if (!mcr.changesMade) {
                /**
                 * 正如文章所说, changesMade默认为false, mModified数据被写入到mMap时将changesMade
                 * 置为true, 所以如果数据没有发生变化, 则不对File做任何处理;
                 */
                mcr.setDiskWriteResult(true);
                return;
            }
        }
        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);
            final StructStat stat = Os.stat(mFile.getPath());
            synchronized (this) {
                mStatTimestamp = stat.st_mtime;
                mStatSize = stat.st_size;
            }
            mBackupFile.delete();
            mcr.setDiskWriteResult(true);
            return;
        } 
        catch (XmlPullParserException e) {...} 
        catch (IOException e) {...}
        mcr.setDiskWriteResult(false);
    }
    
    五、EditorImpl.apply:
    5.1 EditorImpl.apply:
    public void apply() {
        /**
         * 将mModified数据写入到MemoryCommitResult中;
         */
        final MemoryCommitResult mcr = commitToMemory();
        final Runnable awaitCommit = new Runnable() {
            public void run() {
                mcr.writtenToDiskLatch.await();
            }
        };
        QueuedWork.add(awaitCommit);
        Runnable postWriteRunnable = new Runnable() {
            public void run() {
                awaitCommit.run();
                QueuedWork.remove(awaitCommit);
            }
        };
        /**
         * 将MemoryCommitResult中缓存的数据写入到磁盘中;
         */
        SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);    模块<4.3>
        notifyListeners(mcr);
    }
    

    相关文章

      网友评论

          本文标题:源码学习->18SharedPreferences

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