原理
通过 ContentProvider 和 ContentObserver实现的
调用方式
改值
void test() {
Settings.Global.putString(contentResolver, name, value);
}
取值
void test() {
Settings.Global.getString(contentResolver, name);
}
监听变化
void test() {
contentResolver.registerContentObserver(Settings.Global.getUriFor(那个settings的名字), //uri是把name拼接到settings/global/后
isNotifyForDescendants, contentObserver);
}
存值
app端
说明
调用服务端SettingsProvider.call(…)方法来存值
代码流程
通过NameValueCache实现的。每个区域有自己的NameValueCache。
import android.provider;
class Settings {
class Global extends NameValueTable {
String putStringForUser(...) {
...
return sNameValueCache.putStringForUser(...);
}
}
}
NameValueCache中,使用IContentProvider.call(…),向SettingsProvider里存值。
class Settings {
static class NameValueCache {
boolean putStringForUser(., String name, String value, ...) {
...
// 把值保存到Bundle里,再传给SettingsProvider
Bundle arg = new Bundle();
arg.putString("value", value);
...
// 获取到SettingsProvider 的 Binder
IContentProvider cp = mProviderHolder.getProvider(cr);
// mUri.authority = "settings"
// mCallSetCommand = "PUT_global"
cp.call(., mProviderHolder.mUri.getAuthority(), mCallSetCommand, name, arg);
}
}
}
service端
说明
SettingsProvider接收到app端的call(…)调用后:
- 把(name, value)保存到SettingsState对象里,SettingsState就对应着一个xml
- 把(name, value)保存到xml文件
- 修改 MemoryIntArray里的值
- 调用notify()通知各个ContentObserver
代码流程
调用链是SettingsProvider.call() -> insertGlobalSeting() -> mutateGlobalSetting() ->
SettingsProvider.SettingsRegistry.insertSettingLocked() -> insertSettingLock()
最终是在insertSettingLock(…)中做的各种事情
class SettingsProvider {
// 继续保存value
class SettingsRegistry {
boolean insertSettingLocked(...) {
// type 表示哪个xml,global、security、system..
// 所以key就可以解析到用哪个xml。
int key = makeKey(type, userId);
// 通过key获取使用哪个xml。比如users/0/settings_global.xml,把文件解析到这个settingsState里。
// 下面代码时把name-value保存到SettingsState中、同时写入xml文件
SettingsState settingsState = peekSettingsStateLocked(key);
if (settingsState != null) {
success = settingsState.insertSettingLocked(name, value,
tag, makeDefault, forceNonSystemPackage, packageName);
}
if (forceNotify || success) {
// 每个key代表一个xml,每个xml在MemoryIntArray中有一个index。下面会对MemoryIntArray[index]的值+1。
// 调用ContentResolover.notifyChange(uri, ..),通知ContentObserver。
notifyForSettingsChange(key, name);
}
}
}
}
取值
app端
说明
(name, value)是存在xml文件中的,第一次取值会调用IContentProvider.call(…)方法。
取到后,会把(name, value)缓存。
再对name取值时,先到缓存取,如果缓存有保存此(name, value),且它所在的xml没有变过,则直接返回value。
“xml文件是否变过”,是用MemoryIntArray实现的,它直接读写内存,可跨进程。
代码流程
settings是通过NameValueCache来取值的
import android.provider;
class Settings {
class Global extends NameValueTable {
String getStringForUser(...) {
...
return sNameValueCache.getStringForUser(...);
}
}
}
NameValueCache 是通过contentProvider.call() 取值的
class Settings {
static class NameValueCache {
String getStringForUser(ContentResolver cr, String name, final int userHandle) {
if (self) {
if (mGenerationTracker != null) {
if (mGenerationTracker.isGenerationChanged) { //对应的xml没有变化
} else if (mValues.containsKey(name)) { // 缓存里有
return mValues.get(name); //对应xml文件没有变化,且缓存有。直接返回
}
}
}
// 获取到SettingsProvider。通过binder取值
IContentProvider cp = mProvider.getProvider(cr);
if (mCallGetCommand != null) {
try {
...
if (Settings.isInSystemServer() && .) { // SettingsProvider 会设置一个static的值
} else {
// 调用IContentProvider.call() 接口,mCallGetCommand="GET_global",args是个null的Bundle。uri="content://settings/global",是在 authorities="settings"的contentProvider里取值,也就是SettingsProvider
b = cp.call(cr.getPackageName(), mProviderHolder.mUri.getAuthority(), mCallGetCommand, name, args);
}
if (b != null) {
String value = b.getString("value"); // 从Bundle里取值
if (self) {
if (needGenerationTracker) {//第一次取某个xml里的值,保存它的MemoryIntArray、index、generation
MemoryIntArray array = b.getParcelable(某个key);
...
mGenerationTracker = new Generation(array, index, generation, ()->{});
}
if (mGeneration != null && currentGeneration == mGenerationTracker.getCurrentGEeneration()) {
mValues.put(name, value); //缓存起来
}
}
return value;
}
}
}
}
}
}
System端
说明
SettingsProvider接收到app端的call(…)调用后:
- 查询name对应的value
- 如果app端需要MemoryIntArray,则创建
1. MemoryIntArray
2. (name, value)对应xml文件在MemoryIntArray中的index
3. xml当前的generation - 把 (1)(2)里的值,返回给app端
代码
在frameworks/base/packages/SettingsProvider/AndroidManifest.xml中有声明。
<provider android:name="SettingsProvider"
android:authorities="settings"
android:multiprocess="false"
android:exported="true"
android:singleUser="true"
android:initOrder="100"
android:visibleToInstantApps="true" />
读写/data/system/users/0/settings_global.xml这个文件
class SettingsProvider extends ContentProvider {
Bundle call(String method, String name, Bundle args) {
switch (method) {
...
case Settings.CALL_METHOD_GET_GLOBAL: // GET_global
Setting setting = getGlobalSetting(name); //settings_global.xml文件会解析到SettingsState,每个(name,value)是Setting对象
// isTrackingGeneration() 此次app是否要返回MemoryIntArray
// packageValueFroCallResult(..) 包含了value值和 用于判断缓存是否有效的MemoryIntArray。
return packageValueForCallResult(setting, isTrackingGeneration(args));
}
}
}
创建MemoryIntArray,并保存到Bundle里
class GenerationRegistry {
// bundle 中保存了要get的value
// key 是type+userId,可以判断出是哪个xml文件
void addGenerationData(Bundle bundle, int key) {
synchronized (mLock) {
try {
// 第一次会创建size(=1+2+10+2*maxUsersCount) * 32bit的内存用来存储 settings_global、settings_security、 settings_system、 其他用户的 settings_* 。
// SettingsProvider端只有一个MemoryIntArray实例。
MemoryIntArray backingStore = getBackingStoreLocked();
if (backingStore != null) {
// key = type+userId, type是 global或system或security。
// 返回值index,表示xml的修改状态保存在MemoryIntArray[index]中
int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore);
// 给app返回
// 1, MemoryIntArray,这是个parcelable。
// 2, 此(name, value)存放的xml,对应MemoryIntArray的第几个32bit数据
// 3, backingStore.get(index), 是(2)中32bit数据的值。xml有变,则这个值就会变。
if (index > 0) {
bundle.putParcelable(., backingStore);
bundle.putInt(., index);
bundle.putInt(., backingStore.get(index));
}
}
}
}
}
}
MemoryIntArray
class MemoryIntArray {
// 创建size大小的内存
public MemoryIntArray(int size) {
mFd = nativeCreate(name, size);
mMemoryAddr = nativeOpen(mFd, mIsOwner);
}
void set(int index, int value) {
...
nativeSet(mFd, mMemory, index, value); //native方法,修改对应内存的值
}
int get(int index) {
return nativeGet(mFd, mMemoryAddr, index);
}
}