SharedPreferences簡介:適用于保存少量數(shù)據(jù)帝美,且這些數(shù)據(jù)的格式都是基本類型的值潦蝇。保存數(shù)據(jù)基于XML文件存儲的key-value鍵值對數(shù)據(jù)番捂。SharedPreferences對象本身只能獲取數(shù)據(jù)用踩,而不支持存儲和修改渠退,存儲和修改是通過SharedPreferences.edit()獲取的內(nèi)部類接口Editor對象實現(xiàn)。SharedPreferences本身是一個接口脐彩,應(yīng)用程序無法創(chuàng)建SharedPreferences實例碎乃,只能通過Context提供的getSharedPreferences(String name, int mode)方法來獲取SharedPreferences實例。
Editor基礎(chǔ)方法
- putString:保存一個鍵值對
- remove:刪除一個鍵值對
- clear:清空SP數(shù)據(jù)
- commit:同步提交修改數(shù)據(jù)
- apply:異步提交修改數(shù)據(jù)
一惠奸、SharedPreferences基本使用
public class MainActivity extends AppCompatActivity {
@SuppressLint("CommitPrefEdits")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//MODE_PRIVATE數(shù)據(jù)只能被本應(yīng)用程序讀梅誓、寫
//MODE_WORLD_READABLE數(shù)據(jù)能被其他應(yīng)用程序讀,但不能寫
//MODE_WORLD_WRITEABLE數(shù)據(jù)能被其他應(yīng)用程序讀,寫
SharedPreferences sharedPreferences = getSharedPreferences("name", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
//存儲數(shù)據(jù)
editor.putString("key", "value");
editor.apply();
//獲取數(shù)據(jù)
sharedPreferences.getString("key", "");
//獲取其他應(yīng)用的SharedPreferences數(shù)據(jù)
Context otherAppContent = null;
try {
//設(shè)置我們要調(diào)用數(shù)據(jù)的應(yīng)用包名
otherAppContent = createPackageContext("com.example.datastorageframe", CONTEXT_IGNORE_SECURITY);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
assert otherAppContent != null;
@SuppressLint("WorldReadableFiles")
SharedPreferences otherSharedPreferences = otherAppContent.getSharedPreferences("test", MODE_WORLD_READABLE);
otherSharedPreferences.getString("name", "");
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
創(chuàng)建SharedPreferences工具類
public class SharedPreferencesUtils {
public static final String name = "test";
private static SharedPreferencesUtils instance;
public static SharedPreferencesUtils getInstance() {
if (instance == null) {
synchronized (SharedPreferencesUtils.class) {
if (instance == null) {
instance = new SharedPreferencesUtils();
}
}
}
return instance;
}
/**
* 保存數(shù)據(jù)梗掰,修改數(shù)據(jù)
*
* @param key
* @param value
* @param <V>
*/
public <V> void setValue(@NonNull String key, V value) {
SharedPreferences sp = MyApp.getInstance().getSharedPreferences(name, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
if (value instanceof String) {
editor.putString(key, (String) value);
} else if (value instanceof Integer) {
editor.putInt(key, (Integer) value);
} else if (value instanceof Long) {
editor.putLong(key, (Long) value);
} else if (value instanceof Boolean) {
editor.putBoolean(key, (Boolean) value);
} else if (value instanceof Float) {
editor.putFloat(key, (Float) value);
}
editor.commit();
}
/**
* 讀取數(shù)據(jù)
*
* @param key
* @param defaultValue
* @param <V>
* @return
*/
public <V> V getValue(@NonNull String key, V defaultValue) {
SharedPreferences sp = MyApp.getInstance().getSharedPreferences(name, Context.MODE_PRIVATE);
Object value = defaultValue;
if (defaultValue instanceof String) {
value = sp.getString(key, (String) defaultValue);
} else if (defaultValue instanceof Integer) {
value = sp.getInt(key, (Integer) defaultValue);
} else if (defaultValue instanceof Long) {
value = sp.getLong(key, (Long) defaultValue);
} else if (defaultValue instanceof Boolean) {
value = sp.getBoolean(key, (Boolean) defaultValue);
} else if (defaultValue instanceof Float) {
value = sp.getFloat(key, (Float) defaultValue);
}
return (V) value;
}
/**
* 清空數(shù)據(jù)
*/
public void clearData() {
SharedPreferences.Editor editor = MyApp.getInstance().
getSharedPreferences(name, Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
}
}
優(yōu)點:SharedPreferences對象與SQLite數(shù)據(jù)庫相比顯得格外輕量級嵌言,免去了創(chuàng)建數(shù)據(jù)庫,創(chuàng)建表及穗,寫SQL語句等諸多操作摧茴,相對而言更加方便,簡潔埂陆。
缺點:只能存儲boolean苛白,int,float焚虱,long和String五種簡單的數(shù)據(jù)類型购裙。無法進(jìn)行條件查詢等。
二著摔、源碼解析
(1)getSharedPreferences源碼
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
//判空處理
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}
File file;
synchronized (ContextImpl.class) {
if (mSharedPrefsPaths == null) {
mSharedPrefsPaths = new ArrayMap<>();
}
//根據(jù)名字取文件
file = mSharedPrefsPaths.get(name);
if (file == null) {
//根據(jù)名字創(chuàng)建文件
file = getSharedPreferencesPath(name);
//保存到ArrayMap中
mSharedPrefsPaths.put(name, file);
}
}
//根據(jù)文件獲取sp對象
return getSharedPreferences(file, mode);
}
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
//sp實現(xiàn)類
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
//根據(jù)包名獲取ArrayMap<File, SharedPreferencesImpl>
final ArrayMap<File, SharedPreferencesImpl> cache = getSharedPreferencesCacheLocked();
sp = cache.get(file);
if (sp == null) {
checkMode(mode);
if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
if (isCredentialProtectedStorage()
&& !getSystemService(UserManager.class)
.isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
throw new IllegalStateException("SharedPreferences in credential encrypted "
+ "storage are not available until after user is unlocked");
}
}
//實例化sp缓窜,并保存到緩存中
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) {
//如果是多進(jìn)程模式需要重新讀取文件
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}
(2)SharedPreferencesImpl源碼
SharedPreferencesImpl(File file, int mode) {
mFile = file;
//備份file
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
mThrowable = null;
startLoadFromDisk();
}
private void startLoadFromDisk() {
synchronized (mLock) {
mLoaded = false;
}
//開啟一個線程從磁盤讀取數(shù)據(jù)
new Thread("SharedPreferencesImpl-load") {
public void run() {
loadFromDisk();
}
}.start();
}
private void loadFromDisk() {
synchronized (mLock) {
//是否讀取過
if (mLoaded) {
return;
}
//如果備份文件存在定续,刪除mFile谍咆,將備份文件重命名給mFile
if (mBackupFile.exists()) {
mFile.delete();
mBackupFile.renameTo(mFile);
}
}
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);
//將xml文件轉(zhuǎn)成map
map = (Map<String, Object>) XmlUtils.readMapXml(str);
} catch (Exception e) {
} finally {
IoUtils.closeQuietly(str);
}
}
} catch (ErrnoException e) {
} catch (Throwable t) {
thrown = t;
}
synchronized (mLock) {
//表示已經(jīng)讀取過,下次調(diào)用getSharedPreferences不會再從磁盤讀取
mLoaded = true;
mThrowable = thrown;
try {
if (thrown == null) {
if (map != null) {
//賦值給成員變量mMap
mMap = map;
mStatTimestamp = stat.st_mtim;
mStatSize = stat.st_size;
} else {
mMap = new HashMap<>();
}
}
} catch (Throwable t) {
mThrowable = t;
} finally {
//釋放鎖
mLock.notifyAll();
}
}
}
@Override
@Nullable
public String getString(String key, @Nullable String defValue) {
synchronized (mLock) {
//阻塞等待sp將xml讀取到內(nèi)存后再get
awaitLoadedLocked();
String v = (String)mMap.get(key);
//如果value為空返回默認(rèn)值
return v != null ? v : defValue;
}
}
@GuardedBy("mLock")
private void awaitLoadedLocked() {
if (!mLoaded) {
BlockGuard.getThreadPolicy().onReadFromDisk();
}
//sp讀取完成后會把mLoaded設(shè)置為true
while (!mLoaded) {
try {
mLock.wait();
} catch (InterruptedException unused) {
}
}
if (mThrowable != null) {
throw new IllegalStateException(mThrowable);
}
}
(3)Editor源碼
@Override
public Editor edit() {
synchronized (mLock) {
awaitLoadedLocked();
}
//每次都會返回一個新的Editor
return new EditorImpl();
}
public final class EditorImpl implements Editor {
//code...
//將鍵值對寫入mModified
@Override
public Editor putString(String key, @Nullable String value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
@Override
public Editor putInt(String key, int value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
@Override
public Editor putLong(String key, long value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
@Override
public Editor putFloat(String key, float value) {
synchronized (mEditorLock) {
mModified.put(key, value);
return this;
}
}
@Override
public Editor putBoolean(String key, boolean 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 Editor clear() {
synchronized (mEditorLock) {
//根據(jù)這個判斷是否clear
mClear = true;
return this;
}
}
@Override
public void apply() {
final long startTime = System.currentTimeMillis();
final MemoryCommitResult mcr = commitToMemory();
final Runnable awaitCommit = new Runnable() {
@Override
public void run() {
try {
mcr.writtenToDiskLatch.await();
} catch (InterruptedException ignored) {
}
}
};
QueuedWork.addFinisher(awaitCommit);
Runnable postWriteRunnable = new Runnable() {
@Override
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
}
@Override
public boolean commit() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
//提交到內(nèi)存中私股,并返回mcr對象
MemoryCommitResult mcr = commitToMemory();
//放入寫入隊列
SharedPreferencesImpl.this.enqueueDiskWrite(
mcr, null /* sync write on this thread okay */);
try {
//線程等待摹察,直到寫入文件操作后
mcr.writtenToDiskLatch.await();
} catch (InterruptedException e) {
return false;
} finally {
}
//通知監(jiān)聽
notifyListeners(mcr);
//返回提交結(jié)果
return mcr.writeToDiskResult;
}
private MemoryCommitResult commitToMemory() {
long memoryStateGeneration;
List<String> keysModified = null;
Set<OnSharedPreferenceChangeListener> listeners = null;
Map<String, Object> mapToWriteToDisk;
synchronized (SharedPreferencesImpl.this.mLock) {
if (mDiskWritesInFlight > 0) {
//這個時候說明正在寫入文件,無法修改倡鲸,寫入文件之后會重置為0
mMap = new HashMap<String, Object>(mMap);
}
mapToWriteToDisk = mMap;
mDiskWritesInFlight++;
boolean hasListeners = mListeners.size() > 0;
if (hasListeners) {
keysModified = new ArrayList<String>();
listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet());
}
synchronized (mEditorLock) {
boolean changesMade = false;
//調(diào)用了clear方法的操作
if (mClear) {
if (!mapToWriteToDisk.isEmpty()) {
changesMade = true;
mapToWriteToDisk.clear();
}
mClear = false;
}
for (Map.Entry<String, Object> e : mModified.entrySet()) {
String k = e.getKey();
Object v = e.getValue();
//this就是為了判斷remove的
if (v == this || v == null) {
if (!mapToWriteToDisk.containsKey(k)) {
continue;
}
mapToWriteToDisk.remove(k);
} else {
if (mapToWriteToDisk.containsKey(k)) {
Object existingValue = mapToWriteToDisk.get(k);
//未發(fā)生修改跳過
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mapToWriteToDisk.put(k, v);
}
changesMade = true;
if (hasListeners) {
keysModified.add(k);
}
}
//將修改的map置空
mModified.clear();
if (changesMade) {
//當(dāng)前提交到內(nèi)存的次數(shù)
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
//將提交內(nèi)存次數(shù)供嚎,發(fā)生修改的集合,監(jiān)聽峭状,整個map封裝成MemoryCommitResult
return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners,
mapToWriteToDisk);
}
//code...
}
private void enqueueDiskWrite(final MemoryCommitResult mcr,
final Runnable postWriteRunnable) {
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
@Override
public void run() {
synchronized (mWritingToDiskLock) {
//寫入文件
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
//寫入完成后減1
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
if (wasEmpty) {
//執(zhí)行寫入文件
writeToDiskRunnable.run();
return;
}
}
//apply操作才會執(zhí)行
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
修改值只會將其存到mModified的map中去克滴,所以在編輯器中所做的所有更改都會批處理,直到我們調(diào)用commit或apply才會設(shè)置到mMap和xml文件中优床。
- SP文件不宜過大劝赔,如果SP文件需要存儲的內(nèi)容過多,可以根據(jù)不同的功能劃分成多個文件胆敞;
- 如果可以的話盡可能早的調(diào)用getSharedPreferences,這樣在調(diào)用put和get操作時着帽,文件已經(jīng)被讀取到內(nèi)存中了;
- 不要多次調(diào)用edit(), 應(yīng)該調(diào)用一次edit(),因為每次調(diào)用edit()都會新建一個Editor;
- 不要多次調(diào)用commit()或apply()移层,如果多次存入值仍翰,應(yīng)該在最后一次調(diào)用。