Analysis of SharedPreferences (1)

Righteousness is clean, flowing in heaven and earth

SharedPreferences is a lightweight storage method often used in Android development. In line with the purpose of "knowing what it is and knowing why it is", let's discuss the implementation process of SharedPreferences.

1. Basic knowledge

  1. SharedPreferences is stored in the form of Key-Value (key-value pair);
  2. SharedPreferences are finally stored on the xml file ;
  3. SharedPreferences is thread safe , but not process safe (MODE_MULTI_PROCESS is a chicken rib.)

2. Basic framework

SharedPreferences is an interface, which contains methods for obtaining data , as well as

  1. Editor interface for storing, deleting and submitting data ;
  2. The OnSharedPreferenceChangeListener interface used to monitor changes in SharedPreferences
public interface SharedPerferences{
    
    

     public interface OnSharedPreferenceChangeListener {
    
    
        /**
         * Called when a shared preference is changed, added, or removed. 
         * (当SharedPreferences更新,添加,删除时被调用)
         * This callback will be run on your main thread.(此回调运行在主线程)
         */
        void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
    }
    public interface Editor{
    
    
    	// 存储操作
        Editor putXXX(String key, XXX value);
        // 删除操作
        Editor remove(String key);
        
        Editor clear();
        // 用于提交的commit()和 apply()方法
        boolean commit();
        
        void apply();
    }
    ···
    Map<String, ?> getAll();
    // 获取数据
    XXX getXXX(String key, XXX defValue);
   
    boolean contains(String key);
    // 返回给我们一个Editor,用于执行其存储,更新和提交操作。
    Editor edit();
}

Through the above code, we can see that both SharePerferences and Editor are interfaces, and the main core implementation logic is in their implementation classes.


final class SharedPreferencesImpl implements SharedPreferences {
    
    
	private final File mFile;
    private final File mBackupFile;
    private final int mMode;
    private final Object mLock = new Object();
    private final Object mWritingToDiskLock = new Object();

    @GuardedBy("mLock")
    private Map<String, Object> mMap;

	 @Override
    @Nullable
    public String getString(String key, @Nullable String defValue) {
    
    
        synchronized (mLock) {
    
    
            awaitLoadedLocked();
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
    }

	 @Override
    public Editor edit() {
    
    
        synchronized (mLock) {
    
    
            awaitLoadedLocked();
        }
        return new EditorImpl();
    }

	  public final class EditorImpl implements Editor {
    
    
	  
        private final Object mEditorLock = new Object();
        
		// 通过putXXX()方法,可以看出值都是先保存在mModified里面
        @GuardedBy("mEditorLock")
        private final Map<String, Object> mModified = new HashMap<>();

		 @Override
        public Editor putString(String key, @Nullable String value) {
    
    
            synchronized (mEditorLock) {
    
    
                mModified.put(key, value);
                return this;
            }
        }
		
		 @Override
        public Editor remove(String key) {
    
    
            synchronized (mEditorLock) {
    
    
                mModified.put(key, this);
                return this;
            }
        }
		 @Override
		 public void apply() {
    
    
		 	// 大体流程跟commit()一样,后文讲解
		 }
		 
		 @Override
		 public boolean commit() {
    
    
		 	// 注意这个commitToMemory()方法。以及MemoryCommitResult类
		   final MemoryCommitResult mcr = commitToMemory();
		    
		   	SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
			
			notifyListeners(mcr);
			
            return mcr.writeToDiskResult;
		}
	}
	// 内部类MemoryCommitResult
	private static class MemoryCommitResult {
    
    
        @Nullable final Set<OnSharedPreferenceChangeListener> listeners;
        // 注意这个Map
        final Map<String, Object> mapToWriteToDisk;
        // 这里的CountDownLatch保证线程的执行顺序。
        final CountDownLatch writtenToDiskLatch = new CountDownLatch(1);

        @GuardedBy("mWritingToDiskLock")
        volatile boolean writeToDiskResult = false;
        boolean wasWritten = false;

        void setDiskWriteResult(boolean wasWritten, boolean result) {
    
    
            this.wasWritten = wasWritten;
            writeToDiskResult = result;
            writtenToDiskLatch.countDown();
        }
    }
	
}

By looking at the source code, we will find that the data is basically transferred in three HashMaps, as shown in the following figure:

insert image description here

3. Basic operation

  1. Get data getXXX():

    Get data from mMap of SharedPreferencesImpl ;

  2. Store data putXXX():
    1. First, we need to get the EditorImpl object through edit() ;
    2. Store the data into the mModified variable of EditorImpl through the putXXX() method .
  3. Submit operation commit() or apply():
    1. Synchronize the data in the mMap of SharedPreferencesImpl through the commitToMemory() method , and then transfer the mModified data in EditorImpl to the mapToWriteToDisk variable of MemoryCommitResult .
    2. Call the enqueueDiskWrite() method to write the memoryCommitResult 's mapToWriteToDisk into the XML file.

4. Summary

The overall process of SharedPreferences is sorted out here, and the next article will go deep into the details of the source code for further analysis.

Guess you like

Origin blog.csdn.net/wangcheeng/article/details/123148052