SettingsProvider 之SettingsCache

转载请注明出处:http://blog.youkuaiyun.com/droyon/article/details/35558437


SettingsCache,此类注明了SettingsProvider的缓冲。会缓冲所有的当前请求访问的key值及其value。此缓冲区处在内存中,读取效率较高。

SettingsProvider支持多用户概念,每个用户都有至少三张表(System、Secure),每一张表都存在一个SettingsCache。

1、在手机启动时,会将SettingsProvider对应的数据库中的数据表的内容缓冲进来。

private void fullyPopulateCache(DatabaseHelper dbHelper, String table, SettingsCache cache) {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor c = db.query(
            table,
            new String[] { Settings.NameValueTable.NAME, Settings.NameValueTable.VALUE },
            null, null, null, null, null,
            "" + (MAX_CACHE_ENTRIES + 1) /* limit */);
        try {
            synchronized (cache) {
                cache.evictAll();
                cache.setFullyMatchesDisk(true);  // optimistic
                int rows = 0;
                while (c.moveToNext()) {
                    rows++;
                    String name = c.getString(0);
                    String value = c.getString(1);
                    cache.populate(name, value);
                }
                if (rows > MAX_CACHE_ENTRIES) {
                    // Somewhat redundant, as removeEldestEntry() will
                    // have already done this, but to be explicit:
                    cache.setFullyMatchesDisk(false);
                    Log.d(TAG, "row count exceeds max cache entries for table " + table);
                }
                if (LOCAL_LOGV) Log.d(TAG, "cache for settings table '" + table
                        + "' rows=" + rows + "; fullycached=" + cache.fullyMatchesDisk());
            }
        } finally {
            c.close();
        }
    }

2、SettingsCache extends LruCache<String, Bundle>

/**
     * In-memory LRU Cache of system and secure settings, along with
     * associated helper functions to keep cache coherent with the
     * database.
     * 在内存中缓存System,Secure的设置项,以及相关功能方法来保证和数据库的一致性。
     */
private boolean mCacheFullyMatchesDisk = false;  // has the whole database slurped. 标记内存LRU缓冲是否清空了整个数据库
cache.evictAll();//remove所有元素,回调entryRemoved(true,key,value)

3、putIfAbsent方法

 /**
         * Atomic cache population, conditional on size of value and if
         * we lost a race.
         *
         * @returns a Bundle to send back to the client from call(), even
         *     if we lost the race.
         */
        public Bundle putIfAbsent(String key, String value) {
            Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value);
            if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) {//如果value为null,或者value的长度小于500字符
                synchronized (this) {
                    if (get(key) == null) {
                        put(key, bundle);//bundle可能:NULL_SETTINGS、Bundle.forPair("value", value)
                    }
                }
            }//【value不为null,且value的长度大于500,直接返回bundle,bundle为Bundle.forPair("value", value)】
            return bundle;
        }

4、populate(填充)

/**
         * Populates a key in a given (possibly-null) cache.
         * 填充cache中的关键字
         */
        public static void populate(SettingsCache cache, ContentValues contentValues) {
            if (cache == null) {
                return;
            }
            String name = contentValues.getAsString(Settings.NameValueTable.NAME);
            if (name == null) {
                Log.w(TAG, "null name populating settings cache.");
                return;
            }
            String value = contentValues.getAsString(Settings.NameValueTable.VALUE);
            cache.populate(name, value);
        }

5、检查重复

/**
         * For suppressing duplicate/redundant settings inserts early,
         * checking our cache first (but without faulting it in),
         * before going to sqlite with the mutation.
         * 在插入前,检查重复。在使用sqlite前,先检查cache
         * 是否为重复的值。
         */
        public static boolean isRedundantSetValue(SettingsCache cache, String name, String value) {
            if (cache == null) return false;
            synchronized (cache) {
                Bundle bundle = cache.get(name);
                if (bundle == null) return false;
                String oldValue = bundle.getPairValue();
                if (oldValue == null && value == null) return true;
                if ((oldValue == null) != (value == null)) return false;//神奇代码
                return oldValue.equals(value);
            }
        }


<think>我们正在讨论Unity SettingsProvider的使用方法。SettingsProvider是Unity Editor中的一个重要功能,它允许开发者创建自定义的设置窗口,将项目相关的配置集中管理。根据Unity文档,SettingsProvider通常用于Project Settings或Preferences窗口中。 步骤指南: 1. 创建SettingsProvider类:需要继承自SettingsProvider类,并实现相应的回调方法。 2. 注册SettingsProvider:使用[SettingsProvider]属性或SettingsProvider.GetSettingsProviders()来注册。 3. 绘制GUI:在OnGUI或CreateProvider中使用GUI绘制设置界面。 下面是一个简单的示例,创建一个名为"MyCustomSettings"的设置页面: ```csharp using UnityEditor; using UnityEngine; public class MyCustomSettingsProvider : SettingsProvider { // 使用序列化对象来存储设置 private SerializedObject m_CustomSettings; private static MyCustomSettings _settings; // 构造函数,指定路径和范围(ProjectSettings或Preferences) public MyCustomSettingsProvider(string path, SettingsScope scope = SettingsScope.Project) : base(path, scope) { } // 在设置界面被激活时调用 public override void OnActivate(string searchContext, UnityEngine.UIElements.VisualElement rootElement) { // 确保设置对象存在 if (_settings == null) { _settings = MyCustomSettings.GetOrCreateSettings(); } m_CustomSettings = new SerializedObject(_settings); } // 绘制设置界面 public override void OnGUI(string searchContext) { // 开始绘制 EditorGUILayout.LabelField("My Custom Settings", EditorStyles.boldLabel); EditorGUILayout.PropertyField(m_CustomSettings.FindProperty("someProperty")); // 应用修改 if (m_CustomSettings.ApplyModifiedProperties()) { // 保存设置 MyCustomSettings.SaveSettings(_settings); } } // 注册设置提供者 [SettingsProvider] public static SettingsProvider CreateMyCustomSettingsProvider() { var provider = new MyCustomSettingsProvider("Project/My Custom Settings", SettingsScope.Project); // 可以在这里设置关键字,用于搜索 provider.keywords = GetSearchKeywordsFromGUIContentProperties<MyCustomSettings>(); return provider; } } // 自定义设置数据类,通常使用ScriptableObject来存储 public class MyCustomSettings : ScriptableObject { public int someProperty = 0; private static MyCustomSettings _instance; public static MyCustomSettings GetOrCreateSettings() { if (_instance == null) { // 尝试加载已有的设置 _instance = AssetDatabase.LoadAssetAtPath<MyCustomSettings>("Assets/Editor/MyCustomSettings.asset"); if (_instance == null) { _instance = CreateInstance<MyCustomSettings>(); AssetDatabase.CreateAsset(_instance, "Assets/Editor/MyCustomSettings.asset"); AssetDatabase.SaveAssets(); } } return _instance; } public static void SaveSettings(MyCustomSettings settings) { EditorUtility.SetDirty(settings); AssetDatabase.SaveAssets(); } } ``` 说明: 1. 我们创建了一个`MyCustomSettingsProvider`类,继承自`SettingsProvider`。 2. 在`OnActivate`中,我们初始化设置对象(这里使用ScriptableObject存储)。 3. 在`OnGUI`中,我们绘制设置界面,并使用`SerializedObject`来支持撤销和序列化。 4. 通过`[SettingsProvider]`属性静态方法注册提供者。 5. 设置数据保存在一个ScriptableObject中,放置在Assets/Editor目录下。 注意: - 设置的范围(Scope)可以是`SettingsScope.Project`(项目设置)或`SettingsScope.User`(用户偏好设置,即Preferences)。 - 对于用户偏好设置,通常不需要保存为资产,可以使用EditorPrefs或其他方式存储。 另外,Unity还提供了`SettingsProvider.GetSettingsProviders()`方法,可以在运行时动态添加设置提供者,但通常使用属性注册更简便。 更多高级用法,如使用UI Toolkit绘制界面,可以参考Unity官方文档[^2]。 请确保将设置提供者脚本放在Editor文件夹中。 如果您需要将设置同时显示在Project Settings和Preferences中,可以创建两个不同的提供者,分别指定不同的路径和范围。 相关问题: §§相关问题§§ 1. 如何在Unity中创建用户偏好设置(Preferences)? 2. SettingsProvider如何使用UI Toolkit绘制界面? 3. 如何将自定义设置保存为可共享的配置文件?</think>### Unity SettingsProvider 使用指南 SettingsProvider 是 Unity Editor 扩展的核心功能,允许开发者在 Unity 编辑器的 **Project Settings** 或 **Preferences** 窗口中创建自定义设置面板。以下是完整使用指南: #### 一、基础使用步骤 1. **创建 SettingsProvider 类** ```csharp using UnityEditor; using UnityEngine; public class CustomSettingsProvider : SettingsProvider { // 构造函数指定设置位置 public CustomSettingsProvider(string path, SettingsScope scope = SettingsScope.Project) : base(path, scope) { } // 核心方法:绘制设置界面 public override void OnGUI(string searchContext) { EditorGUILayout.LabelField("自定义设置", EditorStyles.boldLabel); EditorGUILayout.Space(); // 示例:添加设置字段 EditorPrefs.SetInt("MySetting", EditorGUILayout.IntField("数值设置", EditorPrefs.GetInt("MySetting", 0))); } // 注册提供者(关键步骤) [SettingsProvider] public static SettingsProvider CreateProvider() { return new CustomSettingsProvider( "Project/Custom Settings", // 在Project Settings中的路径 SettingsScope.Project // 作用域(项目/用户) ); } } ``` 2. **作用域说明** - `SettingsScope.Project`:存储在 **ProjectSettings** 文件夹(项目共享) - `SettingsScope.User`:存储在用户偏好中(编辑器 → Preferences) 3. **数据存储位置** ```csharp // 项目级存储(推荐) ProjectSettings/MyCustomSettings.asset // 用户级存储 EditorPrefs.SetInt("UserSetting", 123); ``` #### 二、高级功能实现 1. **搜索功能支持** ```csharp public override void OnGUI(string searchContext) { if (!string.IsNullOrEmpty(searchContext) && !this.path.Contains(searchContext)) return; // 过滤非匹配项 // ...绘制设置项 } ``` 2. **序列化对象存储** ```csharp private SerializedObject m_Settings; public override void OnActivate(string searchContext, VisualElement root) { // 加载或创建ScriptableObject m_Settings = new SerializedObject(MySettings.GetOrCreate()); } public override void OnGUI(string searchContext) { SerializedProperty prop = m_Settings.FindProperty("m_Data"); EditorGUILayout.PropertyField(prop); m_Settings.ApplyModifiedProperties(); } ``` 3. **多语言支持** ```csharp [InitializeOnLoad] public static class Localization { static Localization() { L10n.Tr("Custom Settings"); // 注册可翻译文本 } } ``` #### 三、最佳实践 1. **目录结构建议** ``` Assets/ └── Editor/ ├── Settings/ │ ├── CustomSettingsProvider.cs │ └── MySettings.asset // ScriptableObject数据 ``` 2. **调试技巧** - 使用 `EditorWindow.GetWindow<SettingsService>()` 查看注册状态 - 通过 `[MenuItem("Window/Refresh Settings")]` 强制刷新 3. **注意事项** - 脚本必须放在 **Editor 文件夹** - 修改后需重启 Unity 使新设置生效 - 使用 `SettingsProvider.GetSettingsProviders()` 动态注册 完整文档参考 Unity 官方手册:[SettingsProvider API](https://docs.unity3d.com/ScriptReference/SettingsProvider.html) [^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hailushijie

您的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值