目录
- 一、概叙
- 二、基本用法
- 1、获取SharedPreferences对象
- Context 类中的 getSharedPreferences方法
- Activity 类中的 getPreferences()方法
- PreferenceManager 类中的 getDefaultSharedPreferences()方法
- 2、SharedPreferences的基本使用
- 写入数据
- 读取数据
- 删除指定数据
- 清空数据
- 二、commit方法和apply方法的不同
- 1、commit
- 2、apply
- 3、总结
- 三、QueuedWork
- 1、关于延迟磁盘写入
- 2、主线程堵塞ANR问题
一、概叙
- SharedPreferences是一种轻量级的数据存储方式,采用键值对的存储方式。
- SharedPreferences只能存储少量数据,大量数据不能使用该方式存储,支持存储的数据类型有booleans, floats, ints, longs, strings。
- SharedPreferences存储到一个XML文件中的,路径在/data/data//shared_prefs/下,文件名以及存储后面详细讲述。
二、基本用法
1、获取SharedPreferences对象
要创建存储文件或访问已有数据,首先要获取SharedPreferences才能进行操作。
获取SharedPreferences对象有下面三种方式:
Context 类中的 getSharedPreferences方法
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}
它有两个参数,第一个参数 name 指定了SharedPreferences存储的文件的文件名,第二个参数 mode 指定了操作的模式。这种方式获取的对象创建的文件可以被整个应用所有组件使用,有指定的文件名。
操作模式(mode)
- Context.MODE_PRIVATE:指定该SharedPreferences数据只能被本应用程序读、写。这是默认模式。
public static final int MODE_PRIVATE = 0x0000;
- Context.MODE_WORLD_READABLE: 指定该SharedPreferences数据能被其他应用程序读,但不能写。该模式已弃用。
@Deprecated
public static final int MODE_WORLD_READABLE = 0x0001;
- Context.MODE_WORLD_WRITEABLE: 指定该SharedPreferences数据能被其他应用程序写。该模式已弃用。
@Deprecated
public static final int MODE_WORLD_WRITEABLE = 0x0002;
- Context.MODE_APPEND:该模式会检查文件是否存在,存在就将数据写到文件末尾,否则就创建新文件。
public static final int MODE_APPEND = 0x8000;
Activity 类中的 getPreferences()方法
public SharedPreferences getPreferences(@Context.PreferencesMode int mode) {
return getSharedPreferences(getLocalClassName(), mode);
}
这个方法只接收一个操作模式参数,使用这个方法时会自动将当前活动的类名作为 SharedPreferences 的文件名。
PreferenceManager 类中的 getDefaultSharedPreferences()方法
public static SharedPreferences getDefaultSharedPreferences(Context context) {
return context.getSharedPreferences(getDefaultSharedPreferencesName(context),
getDefaultSharedPreferencesMode());
}
private static int getDefaultSharedPreferencesMode() {
return Context.MODE_PRIVATE;
}
这是一个静态方法,它接收一个 Context 参数,并自动使用当前应用程序的包名作为前缀来命名 SharedPreferences 文件。
2、SharedPreferences的基本使用
写入数据
- 创建一个SharedPreferences对象
SharedPreferences sharedPreferences= getSharedPreferences(“data”,Context.MODE_PRIVATE); - 实例化SharedPreferences.Editor对象
SharedPreferences.Editor editor = sharedPreferences.edit(); - 将获取过来的值放入文件
editor.putString(“name”, “Tom”);
editor.putInt(“age”, 28);
editor.putBoolean(“marrid”,false); - 提交
editor.commit();或者editor.apply();
读取数据
SharedPreferences sharedPreferences= getSharedPreferences(“data”, Context .MODE_PRIVATE);
String userId=sharedPreferences.getString(“name”,"");
删除指定数据
editor.remove(“name”);
editor.commit();或者editor.apply();
清空数据
editor.clear();
editor.commit();
二、commit方法和apply方法的不同
1、commit
public boolean commit() {
long startTime = 0;
if (DEBUG) {
startTime = System.currentTimeMillis();
}
//写入到内存
MemoryCommitResult mcr = commitToMemory();
//排队写入磁盘
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, null /* sync write on this thread okay */);
try {
//利用CountDownLatch等待写入磁盘执行完毕
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;
}
private void enqueueDiskWrite(final MemoryCommitResult mcr,final Runnable postWriteRunnable) {
//如果postWriteRunnable为空表示来自commit()方法调用
final boolean isFromSyncCommit = (postWriteRunnable == null);
final Runnable writeToDiskRunnable = new Runnable() {
public void run() {
synchronized (mWritingToDiskLock) {
//写入磁盘
writeToFile(mcr, isFromSyncCommit);
}
synchronized (mLock) {
mDiskWritesInFlight--;
}
if (postWriteRunnable != null) {
postWriteRunnable.run();
}
}
};
if (isFromSyncCommit) {
boolean wasEmpty = false;
synchronized (mLock) {
wasEmpty = mDiskWritesInFlight == 1;
}
//mDiskWritesInFlight表示正在执行的写入磁盘操作的数量
//当commit提交,且mDiskWritesInFlight为1的时候,直接在当前所在线程执行写入磁盘操作
if (wasEmpty) {
writeToDiskRunnable.run();
return;
}
}
//如果正在执行的写入磁盘的操作数量大于1,则把当前任务加入到队列,异步排队执行
//交给QueuedWork,QueuedWork内部维护了一个HandlerThread,一直执行写入磁盘操作。
QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit);
}
注释:
9. 当调用commit()方法之后,首先将编辑的结果同步到内存中。
10. commit()方法调用 enqueueDiskWrite() 方法时,第二个参数postWriteRunnable传入空。这样在enqueueDiskWrite()方法中通过第二个参数是不是空来判断是不是cmomit提交。
11. 当mDiskWritesInFlight(正在执行的写入磁盘操作的数量)为1的时候,直接在当前所在线程执行写入磁盘操作。否则还是异步到QueuedWork中去执行。commit()时,写入磁盘操作会发生在当前线程的说法是不准确的。
12. 每当有一次写入内存操作,既commitToMemory()方法,mDiskWritesInFlight值就会增加1。每当完成一次写入磁盘,既enqueueDiskWrite()方法,mDiskWritesInFlight的值就减1。
2、apply
public void apply() {
final long startTime = System.currentTimeMillis();
//写入到内存
final MemoryCommitResult mcr = commitToMemory();
//创建等待写入到磁盘的Runnable
final Runnable awaitCommit = new Runnable() {
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() {
public void run() {
awaitCommit.run();
QueuedWork.removeFinisher(awaitCommit);
}
};
//加入到队列,排队执行
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
notifyListeners(mcr);
}
3、总结
- 除非你需要关心xml是否写入文件成功,否则你应该在所有调用commit的地方改用apply。
- SharedPreferences在实例化的时候会把SharedPreferences对应的xml文件内容全部读取到内存。
- 对于非多进程兼容的SharedPreferences的读操作是从内存读取的,不涉及IO操作。写入的时候由于内存已经保存了完整的xml数据,然后新写入的数据也会同步更新到内存,所以无论是用commit还是apply都不会影响立即读取。
三、QueuedWork
1、关于延迟磁盘写入
public static void queue(Runnable work, boolean shouldDelay) {
Handler handler = getHandler();
synchronized (sLock) {
sWork.add(work);
if (shouldDelay && sCanDelay) {
handler.sendEmptyMessageDelayed(QueuedWorkHandler.MSG_RUN, DELAY);
} else {
handler.sendEmptyMessage(QueuedWorkHandler.MSG_RUN);
}
}
}
- 当apply()方式提交的时候,默认消息会延迟发送100毫秒,避免频繁的磁盘写入操作。
- 当commit()方式,调用QueuedWork的queue()时,会立即向handler()发送Message。
- 在enqueueDiskWrite()方法中,调用QueuedWork.queue(writeToDiskRunnable, !isFromSyncCommit)加入队列。isFromSyncCommit表示是不是同步提交。
- boolean isFromSyncCommit = (postWriteRunnable == null);从判断方式来看commit是同步提交,apply则不是同步提交。因为commit方法传入的postWriteRunnable为空。
2、主线程堵塞ANR问题
//QueuedWork.java
public static void waitToFinish() {
...
processPendingWork();//执行文件写入磁盘操作
....
}
private static void processPendingWork() {
long startTime = 0;
....
if (work.size() > 0) {
for (Runnable w : work) {
w.run();
}
...
}
waitToFinish()会将,储存在QueuedWork的操作一并处理掉。在Activity的 onPause()、BroadcastReceiver的onReceive()以及Service的onStartCommand()方法之前都会调用waitToFinish()。大家知道这些方法都是执行在主线程中,一旦waitToFinish()执行超时,就会跑出ANR。