在电话中有很多的设置项,因为电话的设置项有些需要在全局使用,所以通过Settings.System.putStringForUser 和 Settings.System.getStringForUser,来写入和读取一些设置项。
1.问题描述
getStringForUser有时候拿到了错误的值。通过一些log,我们发现:出现问题时都是在多个线程同时读写的时候。所以它肯定涉及到了线程安全的问题。
2.Settings原理
代码位置:frameworks/base/core/java/android/provider/Settings.java
通过这种方式存储的数据,其实是以非常简单的方式存储在xml文件中
global文件路径:/data/system/users/0/settings_global.xml
格式:
<setting id="28" name="lock_sound" value="/system/media/audio/ui/Lock.ogg" package="android" />
就是以键值对的方式存的,多加了id和包名。
所以看清它的原理后就明白,getInt之类最终就会转换为getString。
读取数据的流程
/** @hide */
public static String getStringForUser(ContentResolver resolver, String name,
int userHandle) {
android.util.SeempLog.record(android.util.SeempLog.getSeempGetApiIdFromValue(name));
if (MOVED_TO_SECURE.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Secure, returning read-only value.");
return Secure.getStringForUser(resolver, name, userHandle);
}
if (MOVED_TO_GLOBAL.contains(name) || MOVED_TO_SECURE_THEN_GLOBAL.contains(name)) {
Log.w(TAG, "Setting " + name + " has moved from android.provider.Settings.System"
+ " to android.provider.Settings.Global, returning read-only value.");
return Global.getStringForUser(resolver, name, userHandle);
}
return sNameValueCache.getStringForUser(resolver, name, userHandle);
}
可以关注到,最后都是从sNameValueCache读取的数据。sNameValueCache其实就是一个缓存,如果每次读取都需要跨进程,读写文件的话,速度上肯定是不能接受的。原理也是非常简单维护了一个Hashmap
// Must synchronize on 'this' to access mValues and mValuesVersion.
private final HashMap<String, String> mValues = new HashMap<String, String>();
在读取数据时:
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
final boolean isSelf = (userHandle == UserHandle.myUserId());
if (isSelf) {
synchronized (NameValueCache.this) {
if (mGenerationTracker != null) {
if (mGenerationTracker.isGenerationChanged()) {
if (DEBUG) {
Log.i(TAG, "Generation changed for type:"
+ mUri.getPath() + " in package:"
+ cr.getPackageName() +" and user:" + userHandle);
}