settings

原理

通过 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(…)调用后:

  1. 把(name, value)保存到SettingsState对象里,SettingsState就对应着一个xml
  2. 把(name, value)保存到xml文件
  3. 修改 MemoryIntArray里的值
  4. 调用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(…)调用后:

  1. 查询name对应的value
  2. 如果app端需要MemoryIntArray,则创建
    1. MemoryIntArray
    2. (name, value)对应xml文件在MemoryIntArray中的index
    3. xml当前的generation
  3. 把 (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);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值