美文网首页
SharedPreferences的apply和commit之间

SharedPreferences的apply和commit之间

作者: yangweigbh | 来源:发表于2017-09-13 20:53 被阅读245次

    applycommit 都是通过SharedPreferences的editor进行值修改后,保存的函数,它们之间有什么区别呢

    首先看下SharedPreferences的机制

    SharedPreferences的实现在SharedPrefencesImpl

    /frameworks/base/core/java/android/app/SharedPreferencesImpl.java
    
        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);
                    } catch (XmlPullParserException | IOException e) {
                        Log.w(TAG, "getSharedPreferences", e);
                    } finally {
                        IoUtils.closeQuietly(str);
                    }
                }
            } catch (ErrnoException e) {
                /* ignore */
            }
    
            synchronized (SharedPreferencesImpl.this) {
                mLoaded = true;
                if (map != null) {
                    mMap = map;
                    mStatTimestamp = stat.st_mtime;
                    mStatSize = stat.st_size;
                } else {
                    mMap = new HashMap<>();
                }
                notifyAll();
            }
        }
    

    首先从xml中将key, value 读入mMap,之后获取从SharedPreferences中获取key,value值时就直接从mMap中读取。

        public float getFloat(String key, float defValue) {
            synchronized (this) {
                awaitLoadedLocked(); //wait 知道 mLoaded设为true
                Float v = (Float)mMap.get(key);
                return v != null ? v : defValue;
            }
        }
    

    调用edit方法会返回EditorImpl对象

        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 clear() {
                synchronized (this) {
                    mClear = true;
                    return this;
                }
            }
            
    

    EditorImpl使用mModified变量来保存改变的key, value,clear()则会设置mClear变量

            public void apply() {
                final MemoryCommitResult mcr = commitToMemory();
                final Runnable awaitCommit = new Runnable() {
                        public void run() {
                            try {
                                mcr.writtenToDiskLatch.await();
                            } catch (InterruptedException ignored) {
                            }
                        }
                    };
    
                QueuedWork.add(awaitCommit);
    
                Runnable postWriteRunnable = new Runnable() {
                        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);
            }
    

    apply方法会调用commitToMemorymModified中key, value更新到SharedPreferencesmMap中,
    调用enqueueDiskWrite方法将mMap更新到文件中

    首先看看commitToMemory

            // Returns true if any changes were made
            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) {
                        // 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);
                    }
                    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) {
                        if (mClear) {
                            if (!mMap.isEmpty()) {
                                mcr.changesMade = true;
                                mMap.clear();
                            }
                            mClear = false;
                        }
    
                        for (Map.Entry<String, Object> e : mModified.entrySet()) {
                            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) {
                                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;
                            if (hasListeners) {
                                mcr.keysModified.add(k);
                            }
                        }
    
                        mModified.clear();
                    }
                }
                return mcr;
            }
    

    先创建MemoryCommitResult对象,然后根据有没有更新mMap的key,value,来设置MemoryCommitResult.changedMade。另外还要判断有没有正在运行的保存文件的操作(mDiskWritesInFlight),如果有,还要讲mMap复制一份。将mMap保存在MemoryCommitResult.mapToWriteToDisk中,同时增加mDiskWritesInFlight。

    然后调用enqueueDiskWrite

        private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                      final Runnable postWriteRunnable) {
            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);
    
            // Typical #commit() path with fewer allocations, doing a write on
            // the current thread.
            if (isFromSyncCommit) {
                boolean wasEmpty = false;
                synchronized (SharedPreferencesImpl.this) {
                    wasEmpty = mDiskWritesInFlight == 1;
                }
                if (wasEmpty) {
                    writeToDiskRunnable.run();
                    return;
                }
            }
    
            QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
        }
    

    如果是从apply中调用,则postWriteRunnable不为空,isFromSyncCommit为false,在另外的线程上执行writeToDiskRunnable,主要是writeToFile,writeToFile主要是将当前xml重命名成backup file,然后将mMap写进xml,写成功后,删除backup file,写失败的话,就在下次初始化的时候将backup恢复成xml

    再看看commit的实现

            public boolean commit() {
                MemoryCommitResult mcr = commitToMemory();
                SharedPreferencesImpl.this.enqueueDiskWrite(
                    mcr, null /* sync write on this thread okay */);
                try {
                    mcr.writtenToDiskLatch.await();
                } catch (InterruptedException e) {
                    return false;
                }
                notifyListeners(mcr);
                return mcr.writeToDiskResult;
            }
    

    先是更新mMap,然后调用enqueueDiskWrite写文件。最后会调用mcr.writtenToDiskLatch.await()来等待写文件操作的完成。再回到enqueueDiskWrite

            // Typical #commit() path with fewer allocations, doing a write on
            // the current thread.
            if (isFromSyncCommit) {
                boolean wasEmpty = false;
                synchronized (SharedPreferencesImpl.this) {
                    wasEmpty = mDiskWritesInFlight == 1;
                }
                if (wasEmpty) {
                    writeToDiskRunnable.run();
                    return;
                }
            }
    
            QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);
    

    如果是commit调用,isFromSyncCommit为true,然后判断有没有其他apply调用,如果有其他apply调用,则将writeToDiskRunnable放到单独的线程中,等待其他apply完成后再写入,如果没有其他apply调用,则在当前线程运行writeToDiskRunnable

    相关文章

      网友评论

          本文标题:SharedPreferences的apply和commit之间

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