当前位置: 首页 > article >正文

Android Settings 数据库生成、监听与默认值配置

一、Settings 数据库生成机制​

  1. 传统数据库生成(Android 6.0 前)​
  • 路径​:/data/data/com.android.providers.settings/databases/settings.db
  • 创建流程​:
    • SQL 脚本初始化​:通过 sqlite 工具创建数据库文件并执行 SQL 脚本定义表结构(如 secure、system、global)
    • ADB 推送​:生成 .db 文件后,通过 adb push 推送至设备 /data/system/ 目录
    • 重启服务​:执行 adb shell stop && adb shell start 使配置生效
  1. 新版 XML 存储(Android 6.0 及以后)​
  • 数据迁移​:首次启动或恢复出厂设置时,若检测到旧数据库,系统会将其迁移至 XML 文件(如 /data/system/users/0/settings_global.xml)
  • 文件结构​:
    • Global​:settings_global.xml(全局设置):所有的偏好设置对系统的所有用户公开,第三方APP有读没有写的权限;
    • System​:settings_system.xml(用户偏好):包含各种各样的用户偏好系统设置,第三方APP有读没有写的权限;
    • Secure​:settings_secure.xml(安全设置):安全性的用户偏好系统设置,第三方APP有读没有写的权限;
二、数据监听实现
  1. ContentObserver 机制
  • 监听原理​:通过注册 ContentObserver 监听特定 URI 的变动(如 Settings.Global.CONTENT_URI)
// 自定义观察者类
private class SettingsObserver extends ContentObserver {public SettingsObserver(Handler handler) { super(handler); }@Overridepublic void onChange(boolean selfChange, Uri uri) {// 处理设置变更逻辑(如读取新值并更新 UI)int value = Settings.Global.getInt(getContentResolver(), "key_name", 0);}
}
// 注册监听
Uri uri = Settings.Global.getUriFor("key_name");
getContentResolver().registerContentObserver(uri, false, new SettingsObserver(mHandler));

例如 以UUID为例

//首先我们先定义一个数据库的键值Key
//比如我们定义一个保存设备uuid的键值为"device_uuid"
private static final String KEY_DEVICE_UUID = "device_uuid";//保存设备的uuid
Settings.Secure.putString(getContentResolver(), LinspirerToolConstant.KEY_DEVICE_UUID, uuid)//读取设备的uuid
String uuid = Settings.Secure.getString(getContentResolver(), LinspirerToolConstant.KEY_DEVICE_UUID );

获取UUID 的ContentResolver对象

//获取ContentResolver对象
ContentResolver contentResolver = getContentResolver();
//注册监听对应的数据库字段Key
contentResolver.registerContentObserver(Settings.Secure.getUriFor(LinspirerToolConstant.KEY_SHEEPMIE_UUID),true,new SettingsObserver(null));

自定义ContentObserver类,实现数据变化的回调方法

  private final class SettingsObserver extends ContentObserver {  /*** Creates a content observer.* @param handler The handler to run {@link #onChange} on, or null if none.*/public SettingsObserver(Handler handler) {super(handler);}@Overridepublic void onChange(boolean selfChange) {try {String uuid = Settings.Secure.getString(getContentResolver(), KEY_DEVICE_UUID);Log.w(" -- uuid被改变啦!!! == " + uuid);} catch (Throwable e) {e.printStackTrace();}}
}
监听后 当UUID改变contentResolver则会监听到 然后ContentObserver就会做出反应

默认值配置详解

默认值定义文件
  • 路径​:frameworks/base/packages/SettingsProvider/res/values/defaults.xml
<bool name="def_wifi_on">true</bool>       <!-- 默认开启 Wi-Fi -->
<integer name="def_screen_off_timeout">60000</integer>  <!-- 屏幕超时 60 秒 -->
<string name="def_time_12_24">24</string> 设置时间格式 是12还是24
数据库初始化加载
com.android.providers.settings.DatabaseHelper.java onCreate()中​调用了loadSettings()
  • 代码入口​:DatabaseHelper.java 的 loadSettings() 方法

SettingsProvider的启动过程

frameworks\base\packages\settingsprovider\src\com\android\providers\settings\SettingsProvider.java

运行SettingsProvider,和Activity类似,会回调ContentProvider的生命周期方法,首先的,会调用OnCreate()方法,如下:

@Override
public boolean onCreate() {// 标记当前运行在系统服务进程中,确保设置项的正确处理Settings.setInSystemServer();// 同步初始化关键组件,保证线程安全synchronized (mLock) {// 获取用户管理服务(处理多用户场景)mUserManager = UserManager.get(getContext());// 获取全局包管理服务(应用安装/权限管理)mPackageManager = AppGlobals.getPackageManager();// 获取系统配置管理服务(读取/system/etc配置)mSysConfigManager = getContext().getSystemService(SystemConfigManager.class);// 创建后台处理线程(优先级为BACKGROUND)mHandlerThread = new HandlerThread(LOG_TAG, Process.THREAD_PRIORITY_BACKGROUND);mHandlerThread.start();  // 启动线程// 创建Handler绑定线程的LoopermHandler = new Handler(mHandlerThread.getLooper());// 初始化设置注册中心(核心数据结构)mSettingsRegistry = new SettingsRegistry();}// 缓存系统包名和签名(优化后续鉴权性能)SettingsState.cacheSystemPackageNamesAndSystemSignature(getContext());// 同步处理数据迁移synchronized (mLock) {// 迁移旧版本设置数据(兼容性处理)mSettingsRegistry.migrateAllLegacySettingsIfNeededLocked();// 同步SSAID表(AndroidID关联处理)mSettingsRegistry.syncSsaidTableOnStartLocked();}// 通过Handler异步执行(避免阻塞主线程)mHandler.post(() -> {// 注册广播接收器(监听用户切换/包变更等)registerBroadcastReceivers();// 监听用户限制变化(如企业策略更新)startWatchingUserRestrictionChanges();});// 注册系统服务(核心服务暴露)ServiceManager.addService("settings", new SettingsService(this));   // 设置服务入口ServiceManager.addService("device_config", new DeviceConfigService(this));  // 设备配置服务return true;  // 初始化成功
}

在onCreate方法中,

1.先实例化了HandlerThread的对象mHandlerThread,优先级是THREAD_PRIORITY_BACKGROUND

2.然后实例化SettingsRegistry的实例mSettingsRegistry

3.接下来注册了一个广播接收器

SettingsRegistry的实例化过程

再回到onCreate()中

migrateAllLegacySettingsIfNeededLocked() 中

/​**​* 迁移所有遗留设置数据(如果需要)* * 该方法负责将旧版SQLite数据库中的设置项迁移到新版XML存储格式。* 仅在首次启动或检测到未初始化时执行迁移。* * 同步要求:调用时必须持有mLock锁*/
private void migrateAllLegacySettingsIfNeededLocked() {// 检查全局设置文件是否已存在(避免重复迁移)final int key = makeKey(SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);File globalFile = getSettingsFile(key);if (SettingsState.stateFileExists(globalFile)) {return;  // 已迁移则直接返回}// 记录当前构建ID(用于追踪迁移版本)mSettingsCreationBuildId = Build.ID;// 清除调用者身份(以系统权限执行迁移)final long identity = Binder.clearCallingIdentity();try {// 获取所有活跃用户(包括多用户场景)List<UserInfo> users = mUserManager.getAliveUsers();// 遍历每个用户执行迁移final int userCount = users.size();for (int i = 0; i < userCount; i++) {final int userId = users.get(i).id;// 初始化用户数据库帮助类(访问旧版SQLite)DatabaseHelper dbHelper = new DatabaseHelper(getContext(), userId);SQLiteDatabase database = dbHelper.getWritableDatabase();// 执行核心迁移逻辑migrateLegacySettingsForUserLocked(dbHelper, database, userId);// 执行版本升级检查(处理非迁移的增量更新)UpgradeController upgrader = new UpgradeController(userId);upgrader.upgradeIfNeededLocked();// 清理非活跃用户的内存状态(优化资源使用)if (!mUserManager.isUserRunning(new UserHandle(userId))) {removeUserStateLocked(userId, false);}}} finally {// 恢复原始调用者身份Binder.restoreCallingIdentity(identity);}
}

1.首先通过makeKey获得key,这个key就是之前说过的Global、System、Secure三种类型,

2.获得之后通过getSettingsFile方法创建三种类型的File类型实例:

通过getSettingsFile生成的三种文件分别为:

/data/system/users/{id}/settings_global.xml 存放global

/data/system/users/{id}/settings_system.xml 存放 system

/data/system/users/{id}/settings_secure.xml 存放secure

然后 方法中又实例化了一个dbHelper

dbHelper中

@Override
public void onCreate(SQLiteDatabase db) {db.execSQL("CREATE TABLE system (" +"_id INTEGER PRIMARY KEY AUTOINCREMENT," +"name TEXT UNIQUE ON CONFLICT REPLACE," +"value TEXT" +");");db.execSQL("CREATE INDEX systemIndex1 ON system (name);");createSecureTable(db);// Only create the global table for the singleton 'owner/system' userif (mUserHandle == UserHandle.USER_SYSTEM) {createGlobalTable(db);}db.execSQL("CREATE TABLE bluetooth_devices (" +"_id INTEGER PRIMARY KEY," +"name TEXT," +"addr TEXT," +"channel INTEGER," +"type INTEGER" +");");db.execSQL("CREATE TABLE bookmarks (" +"_id INTEGER PRIMARY KEY," +"title TEXT," +"folder TEXT," +"intent TEXT," +"shortcut INTEGER," +"ordering INTEGER" +");");db.execSQL("CREATE INDEX bookmarksIndex1 ON bookmarks (folder);");db.execSQL("CREATE INDEX bookmarksIndex2 ON bookmarks (shortcut);");// Populate bookmarks table with initial bookmarksloadBookmarks(db);// Load initial volume levels into DBloadVolumeLevels(db);// Load inital settings valuesloadSettings(db);
}

其中 通过 db.execSQL分别创建了 system,Secure,Global三个数据库表

然后通过loadVolumeLevels(db);方法 将系统默认的音量设置写到数据库的system的数据表中

LoadVolumeLevels(db):

/​**​* 加载音频音量默认值到系统设置数据库* * 该方法用于初始化或重置音频相关的系统设置值,包括各音频流的默认音量级别* 以及铃声模式影响的音频流配置。这些设置会被存储在系统的SQLite数据库中。* * @param db 可写的系统设置数据库实例*/
private void loadVolumeLevels(SQLiteDatabase db) {// 使用预编译SQL语句提高批量插入效率SQLiteStatement stmt = null;try {// 创建INSERT OR IGNORE语句(避免重复插入)stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value) VALUES(?,?);");// 加载各音频流的默认音量设置(单位:音量等级)loadSetting(stmt, Settings.System.VOLUME_MUSIC,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_MUSIC));      // 媒体音量loadSetting(stmt, Settings.System.VOLUME_RING,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_RING));       // 铃声音量loadSetting(stmt, Settings.System.VOLUME_SYSTEM,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_SYSTEM));     // 系统音量loadSetting(stmt, Settings.System.VOLUME_VOICE,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL));  // 通话音量loadSetting(stmt, Settings.System.VOLUME_ALARM,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_ALARM));      // 闹钟音量loadSetting(stmt, Settings.System.VOLUME_NOTIFICATION,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_NOTIFICATION));// 通知音量loadSetting(stmt, Settings.System.VOLUME_BLUETOOTH_SCO,AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO)); // 蓝牙SCO音量/* * 配置铃声模式影响的音频流(位掩码方式):* - 默认情况下,铃声、通知和系统音量的音频流会受铃声模式影响* - 在非语音设备(如平板)上,音乐音量也会受铃声模式影响* - 在语音设备(如手机)上,仅铃声/通知/系统音量受影响*/int ringerModeAffectedStreams = (1 << AudioManager.STREAM_RING) |(1 << AudioManager.STREAM_NOTIFICATION) |(1 << AudioManager.STREAM_SYSTEM) |(1 << AudioManager.STREAM_SYSTEM_ENFORCED);// 检查设备是否支持语音功能(config_voice_capable)if (!mContext.getResources().getBoolean(com.android.internal.R.bool.config_voice_capable)) {ringerModeAffectedStreams |= (1 << AudioManager.STREAM_MUSIC); // 平板设备增加音乐流}// 将配置写入数据库loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,ringerModeAffectedStreams);// 加载默认的静音影响流配置(来自AudioSystem常量)loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED);} finally {// 确保关闭数据库语句,避免资源泄漏if (stmt != null) stmt.close();}
}

最后调用loadSettings

到现在 以及加载许多默认值写入数据库中,这些默认值很大一部分定义在defaults.xml文件中,loadVolumlevels和loadSettings()的作用就是在手机第一次启动时,把手机编好设置的默认值写入到数据库settings.db中。

再回到com.android.providers.settings.SettingsProvider.SettingsRegistry.java migrateAllLegacySettingsIfNeededLocked()中

在DatabaseHelper和SQLiteDatabase创建完毕后,调用migrateLegacySettingsForUserLocked方法:

/​**​* 迁移指定用户的遗留设置数据(从SQLite数据库到XML存储)* * 该方法将系统设置、安全设置和全局设置(仅对主用户)从旧版SQLite数据库迁移到新版XML存储格式。* 迁移完成后可选择删除或备份旧数据库。* * @param dbHelper 数据库帮助类实例,提供数据库访问能力* @param database 可写的SQLite数据库实例* @param userId 要迁移的用户ID* * 同步要求:调用时必须持有mLock锁*/
private void migrateLegacySettingsForUserLocked(DatabaseHelper dbHelper,SQLiteDatabase database, int userId) {// ==================== 系统设置迁移 ====================final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);ensureSettingsStateLocked(systemKey);  // 确保系统设置状态已初始化SettingsState systemSettings = mSettingsStates.get(systemKey);migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);  // 执行实际迁移systemSettings.persistSyncLocked();  // 立即持久化到XML文件// ==================== 安全设置迁移 ====================final int secureKey = makeKey(SETTINGS_TYPE_SECURE, userId);ensureSettingsStateLocked(secureKey);  // 确保安全设置状态已初始化SettingsState secureSettings = mSettingsStates.get(secureKey);migrateLegacySettingsLocked(secureSettings, database, TABLE_SECURE);  // 执行实际迁移ensureSecureSettingAndroidIdSetLocked(secureSettings);  // 确保Android ID已正确设置secureSettings.persistSyncLocked();  // 立即持久化到XML文件// ==================== 全局设置迁移(仅主用户) ====================if (userId == UserHandle.USER_SYSTEM) {final int globalKey = makeKey(SETTINGS_TYPE_GLOBAL, userId);ensureSettingsStateLocked(globalKey);  // 确保全局设置状态已初始化SettingsState globalSettings = mSettingsStates.get(globalKey);migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL);  // 执行实际迁移// 如果是新创建的设置文件,记录构建ID(用于追踪迁移版本)if (mSettingsCreationBuildId != null) {globalSettings.insertSettingLocked(Settings.Global.DATABASE_CREATION_BUILDID,mSettingsCreationBuildId, null, true,SettingsState.SYSTEM_PACKAGE_NAME);}globalSettings.persistSyncLocked();  // 立即持久化到XML文件}// ==================== 旧数据库处理 ====================if (DROP_DATABASE_ON_MIGRATION) {dbHelper.dropDatabase();  // 配置为true时直接删除旧数据库} else {dbHelper.backupDatabase();  // 默认行为:备份旧数据库(保留回滚能力)}
}

1.首先调用了 ensureSettingsStateLocked(systemKey);

实例化了settingsState对象指向了sysytem的数据文件,然后将settingsState对象放到mSettingsStates中,然后回到migrateLegacySettingsForUserLocked继续调用migrateLegacySettingsLocked方法

2.再调用了 migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);

/​**​* 执行实际的遗留设置数据迁移(从SQLite表到SettingsState)* * 该方法从指定的SQLite表中读取所有设置项,并将其插入到新的SettingsState存储中。* 这是设置数据从SQLite迁移到XML格式的核心逻辑。* * @param settingsState 目标SettingsState实例(系统/安全/全局)* @param database 包含旧设置的SQLite数据库* @param table 要迁移的源表名(system/secure/global)* * 同步要求:* - 调用时必须持有外部mLock锁* - SettingsState内部会维护自己的同步机制*/
private void migrateLegacySettingsLocked(SettingsState settingsState,SQLiteDatabase database, String table) {// 构建SQL查询(全表扫描)SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();queryBuilder.setTables(table);  // 设置查询表名// 执行查询获取所有设置项(只查询name和value列)Cursor cursor = queryBuilder.query(database, LEGACY_SQL_COLUMNS,null, null, null, null, null);// 空检查if (cursor == null) {return;  // 表不存在或查询失败时提前返回}try {// 检查是否有数据if (!cursor.moveToFirst()) {return;  // 空表直接返回}// 获取列索引(优化后续访问性能)final int nameColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.NAME);final int valueColumnIdx = cursor.getColumnIndex(Settings.NameValueTable.VALUE);// 记录数据库版本(用于兼容性管理)settingsState.setVersionLocked(database.getVersion());// 遍历游标逐条迁移数据while (!cursor.isAfterLast()) {// 读取设置项键值对String name = cursor.getString(nameColumnIdx);String value = cursor.getString(valueColumnIdx);// 插入到新的SettingsState存储settingsState.insertSettingLocked(name,           // 设置项名称(如"volume_music")value,          // 设置项值(如"11")null,           // 默认tag为nulltrue,           // makeDefault设为trueSettingsState.SYSTEM_PACKAGE_NAME);  // 标记为系统设置cursor.moveToNext();  // 移动到下一条记录}} finally {// 确保关闭游标释放资源cursor.close();}
}

先查询出所有System的信息存入游标,然后在循环中作为insertSettingLocked方法的参数

再跳转到insertSettingLocked(String name, String value, String tag,

boolean makeDefault, boolean forceNonSystemPackage, String packageName,

boolean overrideableByRestore)方法中

/​**​* 插入或更新设置项(线程安全)* * 该方法用于修改设置项的当前值和/或默认值,包含内存管理、冲突检测和日志记录等完整逻辑。* 是SettingsProvider核心的写操作方法。* * @param name 设置项名称(如"screen_brightness")* @param value 要设置的新值(如"120")* @param tag 可选标签,用于标记设置来源* @param makeDefault 是否同时设为默认值* @param forceNonSystemPackage 强制允许非系统应用修改* @param packageName 发起修改的包名(用于权限控制)* @param overrideableByRestore 是否允许被系统恢复操作覆盖* @return 是否成功修改* * 同步要求:调用时必须持有mLock锁*/
@GuardedBy("mLock")
public boolean insertSettingLocked(String name, String value, String tag,boolean makeDefault, boolean forceNonSystemPackage, String packageName,boolean overrideableByRestore) {// 1. 参数校验if (TextUtils.isEmpty(name)) {return false;  // 拒绝空key}// 2. 获取旧值状态Setting oldState = mSettings.get(name);String oldValue = (oldState != null) ? oldState.value : null;String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;String newDefaultValue = makeDefault ? value : oldDefaultValue;// 3. 内存使用检查(防止恶意应用耗尽内存)int newSize = getNewMemoryUsagePerPackageLocked(packageName,oldValue == null ? name.length() : 0 /* deltaKeySize */,oldValue, value, oldDefaultValue, newDefaultValue);checkNewMemoryUsagePerPackageLocked(packageName, newSize);// 4. 创建/更新设置项Setting newState;if (oldState != null) {// 4.1 更新现有项(包含权限校验)if (!oldState.update(value, makeDefault, packageName, tag, forceNonSystemPackage,overrideableByRestore)) {return false;  // 权限校验失败}newState = oldState;} else {// 4.2 创建新项newState = new Setting(name, value, makeDefault, packageName, tag,forceNonSystemPackage);mSettings.put(name, newState);}// 5. 记录统计日志(用于分析设置项变更)FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name,                      // 设置项名称value,                     // 新值newState.value,            // 实际设置值(可能被修正)oldValue,                  // 旧值tag,                       // 操作标签makeDefault,               // 是否设为默认getUserIdFromKey(mKey),    // 用户IDFrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);  // 操作类型// 6. 记录历史操作(用于调试和恢复)addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);// 7. 更新内存计数器updateMemoryUsagePerPackageLocked(packageName, newSize);// 8. 触发持久化(异步写入磁盘)scheduleWriteIfNeededLocked();return true;
}

将每个设置项放mSetting中。

所以 ensureSettingsStateLocked(systemKey);到 migrateLegacySettingsLocked(systemSettings, database, TABLE_SYSTEM);方法

首先创建了指向system的XML数据文件的对象SettingsState(com.android.providers.settings.SettingsState), 该对象中存在mSettings(MAP类型),

这个mSetting中封装了所有设置项name、value、packageName的对象setting。

所以settingState拥有了system的XML数据文件的所有设置项

最后再执行 systemSettings.persistSyncLocked();

1.移除旧的消息:从消息队列中移除 MSG_PERSIST_SETTINGS 消息,防止重复写入。

2.立即写入状态:调用 doWriteState() 立即执行设置的持久化操作。

其中doWriteState() 会将当前settingsState拥有的设置项从内存中序列化写入 XML文件中

然后至此结束 后面的方法 都是secure和global的XML文件的处理 最后 XML数据文件都由 SettingsState通过mSetting这个map持有

总结

在SetttngsProvider的启动过程中,会创建数据库,把默认设置的数据值写入数据库,然后将数据库中的数据全部迁移到xml文件中

然后为了通过Settings.java对使用SettingsProvider进行了封装

settings.java代码创建了三个静态内部类,System,Secure,Global,三个类都继承了NameValueCache,每个NameValueCache都有指向SettingsProvider中的SettingsProvider.java的AIDL远程调用IContentProvider(IContentProvider 是 Android 框架内部定义的 AIDL 接口,用于实现 ContentProvider 的跨进程通信。),因此,查询数据需要经过NameValueCache的getStringForUser()方法,插入数据需要经过putStringForUser()方法。同时,NameValueCache还持有一个变量mValues,用于保存查询过的设置项,以便下下次再次发起查询时,能够快速返回。

自动调节亮度为例子:

其对应的Fragment是:DisplaySettings.java;

加载布局:display_settings.xml:

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source ProjectLicensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.
--><PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"xmlns:settings="http://schemas.android.com/apk/res-auto"android:key="display_settings_screen"android:title="@string/display_settings"settings:keywords="@string/keywords_display"><PreferenceCategoryandroid:title="@string/category_name_brightness"><com.android.settingslib.RestrictedPreferenceandroid:key="brightness"android:title="@string/brightness"settings:keywords="@string/keywords_display_brightness_level"settings:useAdminDisabledSummary="true"settings:userRestriction="no_config_brightness"/><com.android.settingslib.PrimarySwitchPreferenceandroid:key="auto_brightness_entry"android:title="@string/auto_brightness_title"android:fragment="com.android.settings.display.AutoBrightnessSettings"settings:controller="com.android.settings.display.AutoBrightnessPreferenceController"/></PreferenceCategory><PreferenceCategoryandroid:title="@string/category_name_lock_display"><Preferenceandroid:key="lockscreen_from_display_settings"android:title="@string/lockscreen_settings_title"android:fragment="com.android.settings.security.LockscreenDashboardFragment"settings:keywords="@string/keywords_ambient_display_screen"settings:controller="com.android.settings.security.screenlock.LockScreenPreferenceController"/><com.android.settingslib.RestrictedPreferenceandroid:key="screen_timeout"android:title="@string/screen_timeout"android:summary="@string/summary_placeholder"android:fragment="com.android.settings.display.ScreenTimeoutSettings"settings:controller="com.android.settings.display.ScreenTimeoutPreferenceController"/></PreferenceCategory><PreferenceCategoryandroid:title="@string/category_name_appearance"><com.android.settings.display.darkmode.DarkModePreferenceandroid:key="dark_ui_mode"android:title="@string/dark_ui_mode"android:fragment="com.android.settings.display.darkmode.DarkModeSettingsFragment"android:widgetLayout="@null"settings:widgetLayout="@null"settings:controller="com.android.settings.display.DarkUIPreferenceController"settings:keywords="@string/keywords_dark_ui_mode"/><Preferenceandroid:fragment="com.android.settings.accessibility.TextReadingPreferenceFragment"android:key="text_reading_options"android:persistent="false"android:title="@string/accessibility_text_reading_options_title"settings:controller="com.android.settings.accessibility.TextReadingFragmentForDisplaySettingsController"/></PreferenceCategory><PreferenceCategoryandroid:title="@string/category_name_color"><com.android.settingslib.PrimarySwitchPreferenceandroid:key="night_display"android:title="@string/night_display_title"android:fragment="com.android.settings.display.NightDisplaySettings"settings:controller="com.android.settings.display.NightDisplayPreferenceController"settings:keywords="@string/keywords_display_night_display"/><Preferenceandroid:key="color_mode"android:title="@string/color_mode_title"android:fragment="com.android.settings.display.ColorModePreferenceFragment"settings:controller="com.android.settings.display.ColorModePreferenceController"settings:keywords="@string/keywords_color_mode"/></PreferenceCategory><PreferenceCategoryandroid:title="@string/category_name_display_controls"><!--Standard auto-rotation preference that will be shown when device state basedauto-rotation settings are NOT available.--><SwitchPreferenceandroid:key="auto_rotate"android:title="@string/accelerometer_title"settings:keywords="@string/keywords_auto_rotate"settings:controller="com.android.settings.display.AutoRotatePreferenceController"/><!--Auto-rotation preference that will be shown when device state based auto-rotationsettings are available.--><Preferenceandroid:key="device_state_auto_rotate"android:title="@string/accelerometer_title"android:fragment="com.android.settings.display.DeviceStateAutoRotateDetailsFragment"settings:keywords="@string/keywords_auto_rotate"settings:controller="com.android.settings.display.DeviceStateAutoRotateOverviewController"/><Preferenceandroid:key="screen_resolution"android:title="@string/screen_resolution_title"android:fragment="com.android.settings.display.ScreenResolutionFragment"settings:keywords="@string/keywords_screen_resolution"settings:controller="com.android.settings.display.ScreenResolutionController"/><SwitchPreferenceandroid:key="display_white_balance"android:title="@string/display_white_balance_title"android:summary="@string/display_white_balance_summary"settings:controller="com.android.settings.display.DisplayWhiteBalancePreferenceController"/><SwitchPreferenceandroid:key="peak_refresh_rate"android:title="@string/peak_refresh_rate_title"android:summary="@string/peak_refresh_rate_summary"settings:controller="com.android.settings.display.PeakRefreshRatePreferenceController"/><SwitchPreferenceandroid:key="show_operator_name"android:title="@string/show_operator_name_title"android:summary="@string/show_operator_name_summary"/><Preferenceandroid:key="screensaver"android:title="@string/screensaver_settings_title"android:fragment="com.android.settings.dream.DreamSettings"settings:keywords="@string/keywords_screensaver"settings:controller="com.android.settings.display.ScreenSaverPreferenceController"/><SwitchPreferenceandroid:key="camera_gesture"android:title="@string/camera_gesture_title"android:summary="@string/camera_gesture_desc"/><SwitchPreferenceandroid:key="lift_to_wake"android:title="@string/lift_to_wake_title"/><SwitchPreferenceandroid:key="tap_to_wake"android:title="@string/tap_to_wake"android:summary="@string/tap_to_wake_summary"/><ListPreferenceandroid:key="theme"android:title="@string/device_theme"android:summary="@string/summary_placeholder"/><Preferenceandroid:key="vr_display_pref"android:title="@string/display_vr_pref_title"android:fragment="com.android.settings.display.VrDisplayPreferencePicker"/></PreferenceCategory>
</PreferenceScreen>

所有的菜单项都在这里;

例如:则对应的是自动亮度(auto_brightness)选项:

com.android.settings.DisplaySettings中

获取该项对应的controller对象并将它添加到controllers中并返回;

该方法返回buildPreferenceControllers():

com.android.settings.display.AutoBrightnessPreferenceController中

状态读取(isChecked):从系统设置读取当前亮度模式,若与默认值不同(即自动模式已启用),返回 true。若没有存储该值,就返回 DEFAULT_VALUE(手动模式)。

状态写入(setChecked)​:根据开关状态,向系统设置写入自动 (1) 或手动 (0) 模式。

获取和修改的实质是对SettingsProvider的操作

设备的亮度模式SCREEN_BRIGHTNESS_MODE_AUTOMATIC(自动)和SCREEN_BRIGHTNESS_MODE_MANUAL(手动)都是通过SettingsProvider来操作的,可以看到自动亮度设置属于System系统属性

由于Settings.java对使用SettingsProvider进行了封装,所以,使用起来相当简单简洁

public static int getInt(ContentResolver cr, String name, int def) {return getIntForUser(cr, name, def, UserHandle.myUserId());
}

在getIntForUser方法中又调用了getStringForUser方法。

相关文章:

Android Settings 数据库生成、监听与默认值配置

一、Settings 数据库生成机制​ ​传统数据库生成&#xff08;Android 6.0 前&#xff09;​​ ​路径​&#xff1a;/data/data/com.android.providers.settings/databases/settings.db​创建流程​&#xff1a; ​SQL 脚本初始化​&#xff1a;通过 sqlite 工具创建数据库文件…...

SeaweedFS S3 Spring Boot Starter

SeaweedFS S3 Spring Boot Starter 源码特性环境要求快速开始1. 添加依赖2. 配置文件3. 使用方式方式一&#xff1a;注入服务类方式二&#xff1a;使用工具类 API 文档SeaweedFsS3Service 主要方法SeaweedFsS3Util 工具类方法 配置参数运行测试构建项目注意事项集成应用更多项目…...

Monorepo架构: 项目管理模式对比与考量

关于 monorepo 相关概念及项目管理模式 在软件开发中&#xff0c;尤其是前端项目&#xff0c;我们会涉及到不同的项目管理模式&#xff0c;这里先介绍几个重要的概念“monorepo”是当前较为热门的一种项目管理方式&#xff0c;虽然很多人可能听说过&#xff0c;但可能在实际项…...

智慧城市项目总体建设方案(Word700页+)

1 背景、现状和必要性 1.1 背景 1.1.1 立项背景情况 1.1.2 立项依据 1.2 现状 1.2.1 党建体系运行现状 1.2.2 政务体系运行现状 1.2.3 社会治理运行现状 1.2.4 安全监管体系现状 1.2.5 环保体系运行现状 1.2.6 城建体系运行现状 1.2.7 社区体系运行现状 1.2.8 园区…...

详解ZYNQ中的 RC 和 EP

详解ZYNQ中的 RC 和 EP 一、ZYNQ FPGA 开发板基础&#xff08; ZC706 &#xff09; 1. 核心特点 双核大脑 灵活积木&#xff1a; ZC706 集成了 ARM Cortex-A9 双核处理器&#xff08;相当于电脑 CPU&#xff09;和 FPGA 可编程逻辑单元&#xff08;相当于可自定义的硬件积木…...

CSP信奥赛C++常用系统函数汇总

# CSP信奥赛C常用系统函数汇总## 一、输入输出函数### 1. cin / cout&#xff08;<iostream>&#xff09; cpp int x; cin >> x; // 输入 cout << x << endl;// 输出 优化&#xff1a;ios::sync_with_stdio(false); 可提升速度 2. scanf() /…...

Qt Quick Dialogs模块功能及架构

Qt Quick Dialogs 是 Qt Quick 的一个附加模块&#xff0c;提供了一套用于创建和使用系统对话框的 QML 类型。在 Qt 6.0 中&#xff0c;这个模块经过了重构和增强。 一、主要功能和特点 1. 对话框类型 Qt Quick Dialogs 在 Qt 6.0 中提供了以下标准对话框类型&#xff1a; …...

Unity基础-Mathf相关

Unity基础-Mathf相关 一、Mathf数学工具 概述 Mathf是Unity中封装好用于数学计算的工具结构体&#xff0c;提供了丰富的数学计算方法&#xff0c;特别适用于游戏开发场景。它是Unity开发中最常用的数学工具之一&#xff0c;能够帮助我们处理各种数学计算和插值运算。 Mathf…...

STM32CubeMX-H7-19-ESP8266通信(中)--单片机控制ESP8266实现TCP地址通信

前言 上篇文章我们已经能够使用串口助手实现esp8266的几种通信&#xff0c;接下来我们使用单片机控制实现。这篇文章会附带教程&#xff0c;增加.c和,.h&#xff0c;把串口和定时器放到对应的编号&#xff0c;然后调用初始化就可以使用了。 先讲解&#xff0c;然后末尾再放源码…...

ubuuntu24.04 编译安装 PostgreSQL15.6+postgis 3.4.2 + pgrouting 3.6.0 +lz4

文章目录 下载基础包下载源码包编译 PG编译 postgis编译安装 pgrouting下载源码包配置编译参数编译安装 初始化数据库建表并检查列是否使用了 lz4 压缩算法检查 postgis 与 pgrouting 是否可以成功创建 下载基础包 sudo apt update && sudo apt upgrade -y sudo apt i…...

【汇编逆向系列】四、函数调用包含单个参数之Double类型-mmword,movsd,mulsd,addsd指令,总结汇编的数据类型

一、汇编代码 上一节开始&#xff0c;讲到了很多debug编译独有的汇编方式&#xff0c;为了更好的区分release的编译器优化和debug的区别&#xff0c;从本章节开始将会提供debug和release的汇编用作对比 Debugb编译 single_double_param:00000000000000A0: F2 0F 11 44 24 08…...

【AI学习】wirelessGPT多任务无线基础模型摘要

收看了关于WirelessGPT多任务无线基础模型的演讲视频&#xff0c;边做一个记录。 应该说&#xff0c;在无线通信大模型的探索方面&#xff0c;有一个非常有益的尝试。 在沈学明院士带领下开展 https://www.chaspark.com/#/live/1125484184592834560...

docker 部署redis集群 配置

docker的网络模式 网桥模式每次重启容器都有可能导致容器ip地址变化&#xff0c;需要固定ip的自己自定义网络&#xff0c;这里介绍的是默认网络模式 docker创建容器 docker run --name redis6379 -p 6379:6379 -p 16379:16379 -v /etc/redis/redis6379:/etc/redis -d --r…...

Ansys Maxwell:线圈和磁体的静磁 3D 分析

本博客展示了如何在 Ansys Maxwell 中执行静磁 3D 分析&#xff0c;以计算载流线圈和永磁体之间相互作用产生的扭矩。在这个例子中&#xff0c;线圈中的电流产生一个沿 Y 轴指向的磁场&#xff0c;而永磁体沿 X 轴被磁化。这种配置导致围绕 Z 轴的扭矩。分步工作流程包括构建几…...

android 之 KeyguardService

一、功能定位与核心作用 KeyguardService 是 Android 锁屏功能的核心服务&#xff0c;负责管理设备锁屏界面&#xff08;如密码、图案、指纹等验证流程&#xff09;&#xff0c;并协调系统安全策略与用户交互。主要职责包括&#xff1a; 锁屏状态管理 控制锁屏界面的显示/隐藏…...

compose 组件 ---无ui组件

在 Jetpack Compose 中&#xff0c;确实存在不直接参与 UI 渲染的组件&#xff0c;它们主要用于逻辑处理、状态管理或副作用控制。这些组件虽然没有视觉界面&#xff0c;但在架构中扮演重要角色。以下是常见的非 UI 组件及其用途&#xff1a; 1. 无 UI 的 Compose 组件分类 (…...

【计算机网络】SDN

SDN这种新型网络体系结构的核心思想&#xff1a;把网络的控制层面与数据层面分离&#xff0c;而让控制层面利用软件来控制数据层面中的许多设备。 OpenFlow协议可以被看成是SDN体系结构中控制层面与数据层面之间的通信接口。 在SDN中取代传统路由器中转发表的是“流表”&…...

HTMLCSS 学习总结

目录 ​​​一、HTML核心概念​​ ​​三大前端技术作用​​ ​​HTML基础结构​​ 开发工具&#xff1a;VS Code 专业配置​​​​安装步骤​​&#xff1a; ​​二、HTML标签大全&#xff08;含表格&#xff09;​​ ​​三、CSS核心技术​​ 1. 三种引入方式对比 2.…...

【设计模式】1.简单工厂、工厂、抽象工厂模式

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 以下是 简单工厂模式、工厂方法模式 和 抽象工厂模式 的 Python 实现与对比&#xff0c;结合代码示例和实际应用场景说明&#xff1a; 1. 简单工厂模式&a…...

成工fpga(知识星球号)——精品来袭

&#xff08;如需要相关的工程文件请关注知识星球&#xff1a;成工fpga&#xff0c;https://t.zsxq.com/DMeqH&#xff0c;关注即送200GB学习资料&#xff0c;链接已置顶&#xff01;&#xff09; 《孩子都能学会的FPGA》系列是成工完成的第一个系列&#xff0c;也有一年多的时…...

spring中的@KafkaListener 注解详解

KafkaListener 是 Spring Kafka 提供的一个核心注解&#xff0c;用于标记一个方法作为 Kafka 消息的消费者。下面是对该注解的详细解析&#xff1a; 基本用法 KafkaListener(topics "myTopic", groupId "myGroup") public void listen(String message)…...

C#调用Rust动态链接库DLL的案例

C#调用Rust动态链接库DLL的案例 项目概述 这是一个演示C#调用Rust动态链接库DLL的项目&#xff0c;包含&#xff1a; C#主程序 (Program.cs)Rust动态链接库 (rust_to_csharp目录) 使用C#创建一个net9的控制台项目&#xff0c;不使用顶级语句 dotnet new console --framewo…...

win11部署suna

参考链接 项目链接 沙盒链接 数据库链接 本文介绍 本文只为项目的辅助&#xff0c;手把手太麻烦 执行步骤 1.下载代码 git clone https://github.com/kortix-ai/suna.git cd suna2.配置环境&#xff08;在Anaconda Prompt上执行&#xff09; python setup.py3.运行代码 …...

Kafka深度解析与原理剖析

文章目录 一、Kafka核心架构原理1. **分布式协调与选举**2. **ISR、OSR与HW机制**3. **高性能存储设计**4. **刷盘机制 (Flush)**5. **消息压缩算法**二、高可用与消息可靠性保障1. **数据高可用策略**2. **消息丢失场景与规避**3. **顺序消费保证**三、Kafka高频面试题精析1. …...

NoSQL——Redis配置与优化

目录 关系型&非关系型数据库 一、核心原理对比‌ ‌二、核心特性对比‌ ‌三、关键区别剖析‌ ‌四、典型产品示例‌ ‌总结‌ Redis Redis核心原理 核心特性 技术意义 配置文件解析 1. 基础配置 2. 持久化配置 3. 内存管理 4. 高可用配置 5. 性能调优 6.…...

CKA考试知识点分享(2)---ingress

CKA 版本&#xff1a;1.32 第二题是涉及ingress相关。本文不是题目&#xff0c;只是为了学习相关知识点做的实验。 1. 环境准备 需要准备一套K8S集群。 1.1 安装ingress-nginx 下载deploy文件&#xff1a; wget -O controller-v1.12.2.yaml https://raw.githubusercontent…...

动态生成element-plus的scss变量;SCSS中实现动态颜色变体生成

文章目录 一、动态css变量1.生成内容2.动态生成css变量2.1新增_color-utils.scss&#xff08;不推荐&#xff09;2.2新增_color-utils.scss&#xff08;推荐&#xff09;2.3theme.scss引入使用 一、动态css变量 1.生成内容 在我们修改element-plus主题色时候&#xff0c;会自…...

Clickhouse统计指定表中各字段的空值、空字符串或零值比例

下面是一段Clickhouse SQL代码&#xff0c;用于统计指定数据库中多张表的字段空值情况。代码通过动态生成查询语句实现自动化统计&#xff0c;处理逻辑如下&#xff1a; 从系统表获取指定数据库&#xff08;替换your_database&#xff09;中所有表的字段元数据根据字段类型动态…...

Linux 内核内存管理子系统全面解析与体系构建

一、前言: 为什么内存管理是核心知识 内存管理是 Linux 内核最核心也最复杂的子系统之一&#xff0c;其作用包括&#xff1a; 为软件提供独立的虚拟内存空间&#xff0c;实现安全隔离分配/回收物理内存资源&#xff0c;维持系统稳定支持不同类型的内存分配器&#xff0c;最优…...

Tableau for mac 驱动

Tableau 驱动程序安装指南 对于希望在 Mac OS 上使用 Tableau 进行数据分析的用户来说&#xff0c;确保正确安装相应的驱动程序至关重要。Tableau 支持多种数据库连接方式&#xff0c;并提供官方文档指导如何设置这些连接。 安装适用于 Mac 的 JDBC 或 ODBC 驱动程序 为了使…...