SoFunction
Updated on 2025-04-07

Android file storage SharedPreferences source code analysis

1. We all know that SharedPreferences is a file that Android can use to store key value.

        SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
         editor = ();
        ("key","value");
        ();
    SharedPreferences sp = getSharedPreferences("fileName", Context.MODE_PRIVATE);
         editor = ();
        ("key","value");
        ();

SharedPreferences is an interface. getSharedPreferences What you get is its implementation class SharedPreferencesImpl.

ArrayMap<String, SharedPreferencesImpl> packagePrefs = (packageName);
public SharedPreferences getSharedPreferences(String name, int mode) {
	   if (sp == null) {
		 File prefsFile = getSharedPrefsFile(name);
		 sp = new SharedPreferencesImpl(prefsFile, mode);
		 (name, sp);
		 return sp;
     }
}

In the constructor, the stored key-value pairs are saved to a hashMap

  SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        mBackupFile = makeBackupFile(file);
        mMode = mode;
        mLoaded = false;
        mMap = null;
        //Read the key value stored in the file and store it in the global variable mMap        startLoadFromDisk();
    }
  private void loadFromDiskLocked() {
    .......
         str = new BufferedInputStream(
                            new FileInputStream(mFile), 16*1024);
         map = (str);
        if (map != null) {
               mMap = map;
               mStatTimestamp = stat.st_mtime;
               mStatSize = stat.st_size;
       } else {
           mMap = new HashMap<String, Object>();
       }
    }

When we getString and other values, we take them from this mMap.

    getThis is the waymapRead in。
public String getString(String key, String defValue) {
        synchronized (this) {
            awaitLoadedLocked();
            String v = (String)(key);
            return v != null ? v : defValue;
        }
    }

2. There are two ways to store data by sharingPrefrence, commit and apply.

() also gets an interface, Editor, and the implementation class is EditorImpl.

  editor = ();
 public Editor edit() {
     return new EditorImpl();
 }

When putString(String key, String value) is called, it is saved to a map first

  private final Map&lt;String, Object&gt; mModified = ();
 public Editor putString(String key, String value) {
        synchronized (this) {
            //Storage the key value to be modified in the map            (key, value);
            return this;
        }
}

So what is the difference between commit and apply?

1).commit has a return value that is a boolean type.

apply has no return value, it is void.

2) commit is synchronous storage, so you must get the return value before the code can go down, otherwise it will be blocked here.

apply is asynchronous storage and is directly thrown into a thread to execute. We don't have to wait for its return result.

Look directly at the source code

  public boolean commit() {
	MemoryCommitResult mcr = commitToMemory();
	(
		mcr, null /* sync write on this thread okay */);
	try {
		();
	} catch (InterruptedException e) {
		return false;
	}
	notifyListeners(mcr);
	return ;
}
public void apply() {
	final MemoryCommitResult mcr = commitToMemory();
	final Runnable awaitCommit = new Runnable() {
			public void run() {
				try {
					();
				} catch (InterruptedException ignored) {
				}
			}
		};
	(awaitCommit);
	Runnable postWriteRunnable = new Runnable() {
			public void run() {
				();
				(awaitCommit);
			}
		};
	(mcr, postWriteRunnable);
	notifyListeners(mcr);
}

Analyze the source code

Both commit and apply call this line of code.

final MemoryCommitResult mcr = commitToMemory();

and private void enqueueDiskWrite(final MemoryCommitResult mcr,

final Runnable postWriteRunnable) ;

The difference between these two lies in the second parameter Runnable postWriteRunnable. The commit is passed a null, and the apply is passed a Runnable object. This parameter is very critical. You will later judge based on this parameter to choose whether to store asynchronously or synchronously.

Let’s first see how commitToMemory() is implemented.

This method is to modify the key value pair (exist in mModified) and the full key value pair in the file (exist in mMap).

Perform comparison and assign the updated map to = mMap;

   private MemoryCommitResult commitToMemory() {
          MemoryCommitResult mcr = new MemoryCommitResult();
           //mMap stores all key-value pairs in the file.            = mMap;
           Traverse the key-value pairs to be added or modified。and add tomMapmiddle
            for (&lt;String, Object&gt; e : ()) {
                       String k = ();
                       Object v = ();
                if ((k)) {
                   Object existingValue = (k);
                   if (existingValue != null &amp;&amp; (v)) {
                       continue;
                   }
               }
               (k, v);
            }
             = true;
            ();
         return mcr;
   }

Looking at the second method enqueueDiskWrite(mrc, runnable).

If it is stored as commit, runnable==null. Then call(); for storage, this method is synchronized.

If it is stored as apply, runnable! =null. It will be directly put into a thread pool for execution.

().execute(writeToDiskRunnable);

This is why apply is asynchronous storage.

  Pay attention to the second parameter,commitThe transmission isnull。applyThe transmission is一个postWriteRunnable
   private void enqueueDiskWrite(final MemoryCommitResult mcr,
                                     final Runnable postWriteRunnable) {
           final Runnable writeToDiskRunnable = new Runnable() {
                   public void run() {
                       synchronized (mWritingToDiskLock) {
                           writeToFile(mcr);
                       }
                       synchronized () {
                           mDiskWritesInFlight--;
                       }
                       if (postWriteRunnable != null) {
                           ();
                       }
                   }
               };
           //Distinguish whether postWriteRunnable is null to distinguish whether it is a commit method or an apply method           final boolean isFromSyncCommit = (postWriteRunnable == null);
           // Typical #commit() path with fewer allocations, doing a write on
           // the current thread.
           //If it is commit method, the above comment also shows that commit is a file storage executed in the current thread.           if (isFromSyncCommit) {
               boolean wasEmpty = false;
               synchronized () {
                   wasEmpty = mDiskWritesInFlight == 1;
               }
               if (wasEmpty) {
                   //Directly call the run method of Runnable.  Executes the storage of files in the current thread.  So it's a synchronization method                   ();
                   return;
               }
           }
            // If it is the applay method, the above code will not be executed, and it will not be returned.            //The method of storing files will be placed in a thread pool for execution           ().execute(writeToDiskRunnable);
       }

Then take a look at writeToFile(MemoryCommitResult mcr). Save the modified key-value pairs into the file.

First, a backup of the source file was made, and then the full amount of the file was written to it.

If the write is successful, the backup file will be deleted.

If an exception occurs while writing a file, the backup file will be restored.

 private void writeToFile(MemoryCommitResult mcr) {
            //Before writing the file, make a backup of the source file first             if (!()) {
                 if (!(mBackupFile)) {
                     (false);
                     return;
                 }
             } else { //If the backup file exists, delete the source file                 ();
             }
          FileOutputStream str = createFileOutputStream(mFile);
            //Save all keyvalues ​​in the file to be modified and save all keyvalues ​​into the new file.            (, str);
            (str);
            ();
            ((), mMode, 0);
            // Writing was successful, delete the backup file if there is one.
            //Delete the backup file            ();
            (true);
 }

This is the article about Android file storage SharedPreferences source code analysis. For more related Android SharedPreferences content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!