简介
SharedPreferences一般用来存储一些简单的数据类型,比如int,String,Boolean
SharedPreferences的内部使用ArrayMap键值对的形式来临时存储数据,最终ArrayMap的数据会通过IO流写入到XML文件中
这个XML文件在手机中的位置是: /data/data/shared_prefs/
1,使用
//获取SharedPreferences 文件名称,模式
SharedPreferences sf = getSharedPreferences("demo",Context.MODE_PRIVATE);
SharedPreferences.Editor mEditor = sf.edit();
//存储
mEditor.putString("name","leidong");
mEditor.putBoolean("man",true);
mEditor.putInt("age",35);
//同步保存到xml文件, //mEditor.apply();另开线程将数据保存在xml
mEditor.commit();
//````````````````````````````
//获取数据
String name = sf.getString("name","");
boolean man = sf.getBoolean("man",true);
int age = sf.getInt("age",0);
Mode:
//私有模式。XML文件每次都覆盖写入
public static final int MODE_PRIVATE = 0x0000;
//文件开放读写权限。不安全,官方已经遗弃这个用法
public static final int MODE_WORLD_READABLE = 0x0001;
public static final int MODE_WORLD_WRITEABLE = 0x0002;
//添加模式。检查文件是否存在,存在就往文件追加内容,不存在就创建新的文件。
public static final int MODE_APPEND = 0x8000
//跨进程模式,官方已经遗弃这个用法
public static final int MODE_MULTI_PROCESS = 0x0004;
2,SharedPreferences 的获取
//Activity中 name默认为类名
public SharedPreferences getPreferences(@Context.PreferencesMode int mode) {
return getSharedPreferences(getLocalClassName(), mode);
}
//ContextWrapper中
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}
Activity,Context,ContextWrapper,ContextImpl之间的关系
ContextImpl.java
private static ArrayMap<String, ArrayMap<File, SharedPreferencesImpl>> sSharedPrefsCache;
//存放xml文件 File
private ArrayMap<String, File> mSharedPrefsPaths;
public SharedPreferences getSharedPreferences(String name, int mode) {
......
//xml文件
File file;
synchronized (ContextImpl.class) {
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();
}
file = mSharedPrefsPaths.get(name);
//如果文件不存在,创建文件bignqie保存在mSharedPrefsPaths中
if (file == null) {
file = getSharedPreferencesPath(name);
mSharedPrefsPaths.put(name, file);
}
}
return getSharedPreferences(file, mode);
}
public File getSharedPreferencesPath(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);
}
throw new IllegalArgumentException(
"File " + name + " contains a path separator");
}
public SharedPreferences getSharedPreferences(File file, int mode) {
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;
}
}
......
return sp;
}
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
}
final String packageName = getPackageName();
ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<>();
sSharedPrefsCache.put(packageName, packagePrefs);
}
return packagePrefs;
}
SharedPreferencesImpl.java
//标记作用,xml是否已经被加载到内存中
private boolean mLoaded = false;
//构造函数,初始化,创建备份文件mBackupFile ,读取磁盘中的xml文件
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);//备份文件
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
startLoadFromDisk();
}
//起一个新线程,加载磁盘的xml文件内容
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
new Thread("SharedPreferencesImpl-load") {
public void run() {
loadFromDisk();
}
}.start();
}
//
private void loadFromDisk() {
synchronized (mLock) {
//如果xml已经被加载过,直接返回
if (mLoaded) {
return;
}
//如果备份存在,说明之前读写操作被中断,把mBackupFile当成mFile来用,直接使用备份文件中的内容。
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<String, Object> map = null;
StructStat stat = null;
Throwable thrown = null;
try {
stat = Os.stat(mFile.getPath());
if (mFile.canRead()) {
BufferedInputStream str = null;
try {
str = new BufferedInputStream(
new FileInputStream(mFile), 16 * 1024);
map = (Map<String, Object>) XmlUtils.readMapXml(str);//读取xml中的内容到内存中,赋值给map
} catch (Exception e) {
Log.w(TAG, "Cannot read " + mFile.getAbsolutePath(), e);
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
// An errno exception means the stat failed. Treat as empty/non-existing by
// ignoring.
} catch (Throwable t) {
thrown = t;
}
synchronized (mLock) {
mLoaded = true;//标记为已经加载过xml文件
mThrowable = thrown;
// It's important that we always signal waiters, even if we'll make
// them fail with an exception. The try-finally is pretty wide, but
// better safe than sorry.
try {
if (thrown == null) {
if (map != null) {
mMap = map;
mStatTimestamp = stat.st_mtim;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
}
// In case of a thrown exception, we retain the old map. That allows
// any open editors to commit and store updates.
} catch (Throwable t) {
mThrowable = t;
} finally {
mLock.notifyAll();//唤醒其他正在等待mLock锁的进程
}
}
}
//获取SharedPreferences中保存的键值对数据
public int getInt(String key, int defValue) {
synchronized (mLock) {
awaitLoadedLocked();//如果mLoaded为false,线程会一直处于等待的状态
Integer v = (Integer)mMap.get(key);//获取读取到内存中的值(mMap键值对)
return v != null ? v : defValue;
}
}
SharedPreferences.Editor对数据的存储操作
private final Map<String, Object> mModified = new HashMap<>();
public Editor edit() {
//如果该线程获取到了mLock对象锁,但是mLoaded为false,也就是加载xml过程没结束,那么线程会一直等待
synchronized (mLock) {
awaitLoadedLocked();
}
return new EditorImpl();
}
//如果mLoaded为false,线程会一直处于等待的状态
private void awaitLoadedLocked() {
if (!mLoaded) {
// Raise an explicit StrictMode onReadFromDisk for this
// thread, since the real read will be in a different
// thread and otherwise ignored by StrictMode.
BlockGuard.getThreadPolicy().onReadFromDisk();
}
while (!mLoaded) {
try {
mLock.wait();
} catch (InterruptedException unused) {
}
}
if (mThrowable != null) {
throw new IllegalStateException(mThrowable);
}
}
//存储的键值对数据只是保存到了内部类Editor的mModified集合中
public Editor putInt(String key, int value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
public boolean commit() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
//把内部类Editor的mModified集合中的数据保存到SharedPreferencesImpl类的mMap集合中。
MemoryCommitResult mcr = commitToMemory();
//把mMap集合中的数据写入到xml文件中
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
if (DEBUG) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " committed after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
notifyListeners(mcr);//通知注册监听者
return mcr.writeToDiskResult;
}
public void apply() {
final long startTime = System.currentTimeMillis();
//把内部类Editor的mModified集合中的数据保存到SharedPreferencesImpl类的mMap集合中。
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
if (DEBUG && mcr.wasWritten) {
Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration
+ " applied after " + (System.currentTimeMillis() - startTime)
+ " ms");
}
}
};
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
//把mMap集合中的数据写入到xml文件中
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);//通知注册监听者
}
//把mMap集合中的数据写入到xml文件中。
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
//如果是apply()形式,则postWriteRunnable != null,isFromSyncCommit 为true。
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
@Override
public void run() {
synchronized (mWritingToDiskLock) {
writeToFile(mcr, isFromSyncCommit);//写入磁盘xml的操作被封装在新起的线程中
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
// Typical #commit() path with fewer allocations, doing a write on
// the current thread.
//如果是commit()的形式,且当前没有写磁盘任务(mDiskWritesInFlight == 1),则直接调用
//writeToDiskRunnable.run()执行writeToFile()写入操作,不起新线程。
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
//如果是apply()的形式,所有的线程都加入到QueuedWork中,以队列的形式保存,逐个线程启动
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}