一,获取SharedPreferences对象
从Activity获取SharedPreferences对象的代码追踪,可以发现其是在ContextWrapper中通过context对象方法获取的。
mBase.getSharedPreferences(name, mode)
那context中是如何创建对象的呢:
1.context对象
Activity中context对象的创建,肯定是要从Activity的创建找起。
ActivityThread中:handleMessage->handleLaunchActivity->performLaunchActivity->createBaseContextForActivity
ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token);
在createBaseContextForActivity中可以看到ContextImpl是创建context的主要类。
2.ContextImpl
return new ContextImpl(null, mainThread,
packageInfo, activityToken, null, false, null, null);
通过createActivityContext方法,可以看到appContext对象时new ContextImpl本身创建的。
由此可以知道,contex是ContextImpl对象实现的。
3.创建SharedPreferences对象
既然知道context对象的实现类,就可以找出SharedPreferences如何创建了。
在getSharedPreferences方法中
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
这段代码可以知道,先获得存储数据的文件,然后创建SharedPreferencesImpl实例,SharedPreferencesImpl就是实现SharedPreferences的实现类。
3.1 获取存储数据的文件
getSharedPrefsFile中:
return makeFilename(getPreferencesDir(), name + ".xml");
getPreferencesDir中:
mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
getDataDirFile中
return mPackageInfo.getDataDirFile();
可以看出,存储数据的文件是在app所在数据目录中创建的一个shared_prefs目录下的文件。简单来说如下:
new File("/data/data/"+context.getPackageName()+"/shared_prefs/"+shareName+".xml")
3.2 获取SharedPreferencesImpl对象
SharedPreferencesImpl(File file, int mode) {
mFile = file;
mBackupFile = makeBackupFile(file);
mMode = mode;
mLoaded = false;
mMap = null;
startLoadFromDisk();
}
makeBackupFile创建了一个备份文件,在startLoadFromDisk则使用线程获取存储文件中的值存储在mMap中,利用备份文件和mLoaded配合使用在多线程下使用SharedPreferences获取存储数据更加安全。
4.获取对象中的参数mode
- Context.MODE_PRIVATE:为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容
- Context.MODE_APPEND:模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件.
- Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用来控制其他应用是否有权限读写该文件.
- MODE_WORLD_READABLE:表示当前文件可以被其他应用读取.
- MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入.
mode主要在writeToFile中存储数据后使用,而writeToFile在enqueueDiskWrite中使用,enqueueDiskWrite主要在apply方法中和commit方法中起作用,而这两个方法是修改数据后提交的方法。所以mode的功能是在写入数据后,更改存储文件的权限作用。
二,使用
1 写入
以下是简单写入操作。
Editor editor = sharedPreferences.edit();
editor.putString(key, value);
editor.commit();
editor.clear();
其中Editor是操作数据写入的主要对象,EditorImpl是其实现类。
put操作是将键值对存入Editor的mModified对象中,mModified是个map对象。
commit(apply)操作是将mModified对象内存中的数据,写入磁盘文件中。
先使用MemoryCommitResult对象存储mModified中的数据,MemoryCommitResult对象主要是方便对数据写入磁盘操作,里面含有操作状态。
下面这个方法是实现数据写入磁盘的方法
SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable);
而这个方法中,writeToFile方法是实现数据的写入。简略为:
......
if (!mBackupFile.exists()) {
if (!mFile.renameTo(mBackupFile)) {
Log.e(TAG, "Couldn't rename file " + mFile
+ " to backup file " + mBackupFile);
mcr.setDiskWriteResult(false);
return;
}
} else {
mFile.delete();
}
//写入数据操作省略
ContextImpl.setFilePermissionsFromMode(mFile.getPath(), mMode, 0);
......
可以看出,mBackupFile又起到了多线程使用安全的作用,而设置的model,在写入完成后,设置了文件的权限。
2.读取数据
一下是简单的获取数据操作
SharedPreferencessharedPreferences= getSharedPreferences(name,
model);
String value =sharedPreferences.getString(key, defValue);
与写入不同,它不需要Editor,直接获取数据mMap对象中的数据。
3.不同应用间读取数据
我们已经知道mode的作用,也知道存储文件是通过context对象从对应的应用文件中创建的。所以要读取其他应用的文件,需要两个条件:
- 要读取应用的context对象,从而获取SharedPreferences对象
- 对应SharedPreferences文件的model要设置为可被其他应用读(写)。
Context otherAppsContext = createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences(name, Context.MODE_WORLD_READABLE);
String value = sharedPreferences.getString(key, defvalue);
总结
SharedPreferences是将数据存储在对应应用目录下shared_prefs文件夹下的xml文件中,通过context获取SharedPreferences对象,通过Editor操作数据的写入,mode设置SharedPreferences的模式。