SharedPreference源码简析

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

    @GuardedBy("mLock")
    private Map<String, Object> mMap;
	...
    SharedPreferencesImpl(File file, int mode) {
        mFile = file;
        mBackupFile = makeBackupFile(file);//创建备份文件
        mMode = mode;
        mLoaded = false;
        mMap = null;
        startLoadFromDisk();//创建SharedPreference对象时,就开始从sd卡中加载文件内容了
    }

    private void startLoadFromDisk() {
        synchronized (mLock) {
            mLoaded = false;
        }
        new Thread("SharedPreferencesImpl-load") {//子线程
            public void run() {
                loadFromDisk();//子线程中去加载文件
            }
        }.start();
    }

    private void loadFromDisk() {
        synchronized (mLock) {
            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);//解析xml文件,把文件中键值对全部读取出来放到Map里
                } catch (Exception e) {
                    Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
                } finally {
                    IoUtils.closeQuietly(str);
                }
            }
        } catch (ErrnoException e) {
            /* ignore */
        }
        synchronized (mLock) {
            mLoaded = true;
            if (map != null) {
                mMap = map;
                mStatTimestamp = stat.st_mtime;
                mStatSize = stat.st_size;
            } else {
                mMap = new HashMap<>();
            }
            mLock.notifyAll();//唤醒其他等待的线程
        }
    }
	...
    private void awaitLoadedLocked() {
        if (!mLoaded) {
            BlockGuard.getThreadPolicy().onReadFromDisk();
        }
        while (!mLoaded) {//只要未加载完成就一直死循环
            try {
                mLock.wait();//阻塞,直到文件加载到内存后mLock.notifyAll()才会唤醒
            } catch (InterruptedException unused) {
            }
        }
    }
	/** api小于11,且支持多进程时,会调该方法,该方法实际上是重新加载解析一次xml到新的进程
	 * 所以SharePreference的跨进程一个是高版本不支持,然后是不是真正的跨进程共享内存数据,只是在新进程重新读一次该文件
	 */
    void startReloadIfChangedUnexpectedly() {
        synchronized (mLock) {
            // TODO: wait for any pending writes to disk?
            if (!hasFileChangedUnexpectedly()) {
                return;
            }
            startLoadFromDisk();
        }
    }

    @Nullable
    public String getString(String key, @Nullable String defValue) {
        synchronized (mLock) {
            awaitLoadedLocked();//使得读取数据的线程休眠(可能是UI线程),直到加载解析文件完成
            String v = (String)mMap.get(key);//返回值
            return v != null ? v : defValue;
        }
    }
	...
	public final class EditorImpl implements Editor {
		public void apply() {
            final long startTime = System.currentTimeMillis();
            final MemoryCommitResult mcr = commitToMemory();
            final Runnable awaitCommit = new Runnable() {
                    public void run() {
                        try {
                            mcr.writtenToDiskLatch.await();//等待写入锁释放
                        } catch (InterruptedException ignored) {
                        }
                    }
                };
            QueuedWork.addFinisher(awaitCommit);//把await()的Runnable放到一个队列里
            Runnable postWriteRunnable = new Runnable() {
                    public void run() {
                        awaitCommit.run();
                        QueuedWork.removeFinisher(awaitCommit);//写入完成,写入锁释放后,从队列移除该任务
                    }
                };
			/* 内部是通过封装了HandlerThread的Handler通知HandlerThread去执行 postWriteRunnable
			 * 也就是写入操作都是通过这个HandlerThread来执行的
			 */
            SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
            notifyListeners(mcr);
        }
	}
}
/**有SharedPreference频繁apply()导致卡顿的原因*/
public final class ActivityThread {
	...
	private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
            return;
        }
        r.activity.mConfigChangeFlags |= configChanges;
        StopInfo info = new StopInfo();
        performStopActivityInner(r, info, show, true, "handleStopActivity");
        updateVisibility(r, show);
        if (!r.isPreHoneycomb()) {
			/* 内部会等队列中所有任务执行完 
			 * SharedPreferences中apply()的任务都会添加到QueuedWork内部队列中
			 * 而处理该队列的线程只有一个HandlerThread
			 * 那么大量的apply()操作会导致这里要等待,从而导致UI卡顿
			 */
            QueuedWork.waitToFinish();
        }
        info.activity = r;
        info.state = r.state;
        info.persistentState = r.persistentState;
        mH.post(info);
        mSomeActivitiesChanged = true;
    }
	...
}

public class XmlUtils {
	public static final HashMap<String, ?> readMapXml(InputStream in) throws XmlPullParserException, java.io.IOException {
        XmlPullParser   parser = Xml.newPullParser();
        parser.setInput(in, StandardCharsets.UTF_8.name());
        return (HashMap<String, ?>) readValueXml(parser, new String[1]);
    }
}
/**
 * 根据下面的代码可知,所有用到的SharedPreferencesImpl会把对应文件的数据加载到内存,且不释放
 */
class ContextImpl extends Context {
	@GuardedBy("ContextImpl.class")
    private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
	
	@Override
    public SharedPreferences getSharedPreferences(File file, int mode) {
        checkMode(mode);
        if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
            if (isCredentialProtectedStorage()
                    && !getSystemService(StorageManager.class).isUserKeyUnlocked(
                            UserHandle.myUserId())
                    && !isBuggy()) {
                throw new IllegalStateException("SharedPreferences in credential encrypted "
                        + "storage are not available until after user is unlocked");
            }
        }
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
            sp = cache.get(file);
            if (sp == null) {
				//创建SharedPreferencesImpl对象时会加载该文件对应所有数据到内存
                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对象数据Map<File,SharedPreferencesImpl>*/
	private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
        if (sSharedPrefsCache == null) {
            sSharedPrefsCache = new ArrayMap<>();
        }
        final String packageName = getPackageName();
		//根据包名,获取当前app用到的所有SharedPreferencesImpl对象
        ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
        if (packagePrefs == null) {
            packagePrefs = new ArrayMap<>();
            sSharedPrefsCache.put(packageName, packagePrefs);
        }
        return packagePrefs;
    }
	
	@Override
    public SharedPreferences getSharedPreferences(File file, int mode) {
        checkMode(mode);
        if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
            if (isCredentialProtectedStorage()
                    && !getSystemService(StorageManager.class).isUserKeyUnlocked(  UserHandle.myUserId())  && !isBuggy()) {
                throw new IllegalStateException("SharedPreferences in credential encrypted "
                        + "storage are not available until after user is unlocked");
            }
        }
        SharedPreferencesImpl sp;
        synchronized (ContextImpl.class) {
            final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
            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) {
            //跨进程 || api低于11时,调用SharedPreference的该方法,在新进程重新加载一次该xml文件,实现所谓的进程共享数据
            sp.startReloadIfChangedUnexpectedly();
        }
        return sp;
    }
}	

总结:
1、Sp不适合存放大量数据:读一个数据,要加载整个文件,且永久存放内存中
2、不能频繁apply():只有一个线程处理写入任务,在onStop()中会等待所有apply()执行完,导致卡顿
3、不适合做进程间共享:官方不建议,且这个进程间共享不过是在新的进程重新读取解析一次文件到内存里,并非共享内存。

猜你喜欢

转载自blog.csdn.net/u010577768/article/details/80528032