getStringForUser原理和线程安全

本文深入探讨了Android Settings.getStringForUser方法存在的线程安全问题。通过分析Settings的存储原理,指出在多线程环境中可能出现的数据同步错误。文章详细描述了问题发生的原因,即由于缓存更新和Generation值的并发问题导致错误的设置值被缓存。为解决这个问题,提出了在写入缓存前再次检查Generation是否变化的建议。
摘要由CSDN通过智能技术生成

在电话中有很多的设置项,因为电话的设置项有些需要在全局使用,所以通过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);
                            }
      
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值