SharedPreferences简介:适用于保存少量数据,且这些数据的格式都是基本类型的值。保存数据基于XML文件存储的key-value键值对数据。SharedPreferences对象本身只能获取数据,而不支持存储和修改,存储和修改是通过SharedPreferences.edit()获取的内部类接口Editor对象实现。SharedPreferences本身是一个接口,应用程序无法创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name, int mode)方法来获取SharedPreferences实例。
Editor基础方法
- putString:保存一个键值对
- remove:删除一个键值对
- clear:清空SP数据
- commit:同步提交修改数据
- apply:异步提交修改数据
一、SharedPreferences基本使用
public class MainActivity extends AppCompatActivity {
@SuppressLint("CommitPrefEdits")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//MODE_PRIVATE数据只能被本应用程序读、写
//MODE_WORLD_READABLE数据能被其他应用程序读,但不能写
//MODE_WORLD_WRITEABLE数据能被其他应用程序读,写
SharedPreferences sharedPreferences = getSharedPreferences("name", MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
//存储数据
editor.putString("key", "value");
editor.apply();
//获取数据
sharedPreferences.getString("key", "");
//获取其他应用的SharedPreferences数据
Context otherAppContent = null;
try {
//设置我们要调用数据的应用包名
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();
}
}
创建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;
}
/**
* 保存数据,修改数据
*
* @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();
}
/**
* 读取数据
*
* @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;
}
/**
* 清空数据
*/
public void clearData() {
SharedPreferences.Editor editor = MyApp.getInstance().
getSharedPreferences(name, Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
}
}
优点:SharedPreferences对象与SQLite数据库相比显得格外轻量级,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。
缺点:只能存储boolean,int,float,long和String五种简单的数据类型。无法进行条件查询等。
二、源码解析
(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<>();
}
//根据名字取文件
file = mSharedPrefsPaths.get(name);
if (file == null) {
//根据名字创建文件
file = getSharedPreferencesPath(name);
//保存到ArrayMap中
mSharedPrefsPaths.put(name, file);
}
}
//根据文件获取sp对象
return getSharedPreferences(file, mode);
}
@Override
public SharedPreferences getSharedPreferences(File file, int mode) {
//sp实现类
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
//根据包名获取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) {
//如果是多进程模式需要重新读取文件
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;
}
//开启一个线程从磁盘读取数据
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文件转成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) {
//表示已经读取过,下次调用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读取到内存后再get
awaitLoadedLocked();
String v = (String)mMap.get(key);
//如果value为空返回默认值
return v != null ? v : defValue;
}
}
@GuardedBy("mLock")
private void awaitLoadedLocked() {
if (!mLoaded) {
BlockGuard.getThreadPolicy().onReadFromDisk();
}
//sp读取完成后会把mLoaded设置为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) {
//根据这个判断是否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();
}
//提交到内存中,并返回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 {
}
//通知监听
notifyListeners(mcr);
//返回提交结果
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;
//调用了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);
//未发生修改跳过
if (existingValue != null && existingValue.equals(v)) {
continue;
}
}
mapToWriteToDisk.put(k, v);
}
changesMade = true;
if (hasListeners) {
keysModified.add(k);
}
}
//将修改的map置空
mModified.clear();
if (changesMade) {
//当前提交到内存的次数
mCurrentMemoryStateGeneration++;
}
memoryStateGeneration = mCurrentMemoryStateGeneration;
}
}
//将提交内存次数,发生修改的集合,监听,整个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) {
//执行写入文件
writeToDiskRunnable.run();
return;
}
}
//apply操作才会执行
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
修改值只会将其存到mModified的map中去,所以在编辑器中所做的所有更改都会批处理,直到我们调用commit或apply才会设置到mMap和xml文件中。
- SP文件不宜过大,如果SP文件需要存储的内容过多,可以根据不同的功能划分成多个文件;
- 如果可以的话尽可能早的调用getSharedPreferences,这样在调用put和get操作时,文件已经被读取到内存中了;
- 不要多次调用edit(), 应该调用一次edit(),因为每次调用edit()都会新建一个Editor;
- 不要多次调用commit()或apply(),如果多次存入值,应该在最后一次调用。