安卓源码学习之【系统属性与 ContentObserver】

1. SystemProperty

1.1 引入 SystemProperties 类

在 Android Framework 中,SystemProperties 类用于访问和操作系统属性。系统属性在 Android 系统中广泛用于存储和检索配置信息。

主要方法

java 复制代码
get(String key)
get(String key, String def)
getInt(String key, int def)
getLong(String key, long def)
getBoolean(String key, boolean def)
set(String key, String value)

1.2 使用示例

获取系统属性

java 复制代码
import android.os.SystemProperties;

public class SystemPropertyExample {
    public static void main(String[] args) {
        // 获取属性值,如果属性不存在则返回空字符串
        String value = SystemProperties.get("ro.build.version.release");
        System.out.println("Android Version: " + value);

        // 获取属性值,如果属性不存在则返回默认值
        String defaultValue = SystemProperties.get("non.existent.property", "default_value");
        System.out.println("Non-existent property: " + defaultValue);
    }
}

设置系统属性

java 复制代码
import android.os.SystemProperties;

public class SystemPropertyExample {
    public static void main(String[] args) {
        // 设置属性值
        SystemProperties.set("custom.property", "custom_value");

        // 验证设置的属性值
        String customValue = SystemProperties.get("custom.property");
        System.out.println("Custom Property: " + customValue);
    }
}

获取整数、长整数和布尔值

java 复制代码
import android.os.SystemProperties;

public class SystemPropertyExample {
    public static void main(String[] args) {
        // 获取整数属性
        int intValue = SystemProperties.getInt("ro.build.version.sdk", 0);
        System.out.println("SDK Version: " + intValue);

        // 获取长整数属性
        long longValue = SystemProperties.getLong("some.long.property", 0L);
        System.out.println("Long Property: " + longValue);

        // 获取布尔值属性
        boolean boolValue = SystemProperties.getBoolean("some.boolean.property", false);
        System.out.println("Boolean Property: " + boolValue);
    }
}

1.3 注意事项

  1. 权限:某些系统属性可能需要特定权限才能读取或修改,特别是在非系统应用中。
  2. 不可滥用:系统属性用于存储全局配置参数,不应滥用或频繁修改,以免影响系统稳定性。
  3. 性能:频繁访问系统属性可能会影响性能,建议将其值缓存到内存中以优化性能。

1.4 总结

SystemProperties 类在 Android Framework 中非常有用,可以方便地获取和设置系统配置参数。虽然它是一个隐藏 API,但在系统开发和调试中非常有帮助。

2. ContentObserver 的使用

ContentObserver 在 Android 开发中常用于监听系统设置的变化,因为系统设置存储在内容提供者(Content Provider)中。

2.1 创建一个 ContentObserver 类

java 复制代码
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;

public class SettingsObserver extends ContentObserver {
    public SettingsObserver(Handler handler) {
        super(handler);
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        // 在这里处理设置变化
        System.out.println("Settings changed: " + uri.toString());
    }
}

2.2 注册 ContentObserver

ActivityService 中注册 ContentObserver

java 复制代码
import android.app.Activity;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;

public class MainActivity extends Activity {
    private SettingsObserver settingsObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler handler = new Handler();
        settingsObserver = new SettingsObserver(handler);

        // 注册观察器,监听系统设置的变化
        getContentResolver().registerContentObserver(
            Settings.System.CONTENT_URI,  // 可选择 Settings.Global.CONTENT_URI 或 Settings.Secure.CONTENT_URI
            true, // 是否监听所有的子 URI
            settingsObserver
        );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (settingsObserver != null) {
            getContentResolver().unregisterContentObserver(settingsObserver);
        }
    }
}

2.3 处理设置变化

SettingsObserveronChange 方法中处理设置变化。例如,如果你只关心特定的设置项,可以根据 URI 进行过滤处理:

java 复制代码
@Override
public void onChange(boolean selfChange, Uri uri) {
    super.onChange(selfChange, uri);
    if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS))) {
        int brightness = Settings.System.getInt(
            context.getContentResolver(),
            Settings.System.SCREEN_BRIGHTNESS,
            -1
        );
        System.out.println("Screen brightness changed: " + brightness);
    }
}

2.4 示例总结

SettingsObserver.java

java 复制代码
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.Settings;

public class SettingsObserver extends ContentObserver {
    private Context context;

    public SettingsObserver(Handler handler, Context context) {
        super(handler);
        this.context = context;
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        if (uri.equals(Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS))) {
            int brightness = Settings.System.getInt(
                context.getContentResolver(),
                Settings.System.SCREEN_BRIGHTNESS,
                -1
            );
            System.out.println("Screen brightness changed: " + brightness);
        }
    }
}

MainActivity.java

java 复制代码
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;

public class MainActivity extends Activity {
    private SettingsObserver settingsObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler handler = new Handler();
        settingsObserver = new SettingsObserver(handler, this);

        getContentResolver().registerContentObserver(
            Settings.System.CONTENT_URI,
            true,
            settingsObserver
        );
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (settingsObserver != null) {
            getContentResolver().unregisterContentObserver(settingsObserver);
        }
    }
}

2.5 注意事项

  1. 监听或操作 SettingsProvider 数据库,需要系统应用权限
xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:sharedUserId="android.uid.system">
  
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
</manifest>
  1. 部分设置项可能需要额外权限或无法被修改

3. 实战演示

修改 MyApplication 代码:

xml 复制代码
public class MainActivity extends AppCompatActivity {

    private SettingsObserver mSettingsObserver;
    private String TEST_KEY = "test_key";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);

        int value = Settings.System.getInt(getContentResolver(),TEST_KEY,-1);
        Log.d("wyd"," onCreate get TEST_KEY value :" + value);
        Settings.System.putInt(getContentResolver(),TEST_KEY,-3);
        mSettingsObserver = new SettingsObserver(new Handler());
        getContentResolver().registerContentObserver(Settings.System.getUriFor(TEST_KEY),true,mSettingsObserver);
    }

    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);
        }


        @Override
        public void onChange(boolean selfChange, @Nullable Uri uri) {
            super.onChange(selfChange, uri);

            int value = Settings.System.getInt(getContentResolver(),TEST_KEY,-1);
            Log.d("wyd"," value : " + value);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getContentResolver().unregisterContentObserver(mSettingsObserver);
    }
}

AndroidManifest.xml 增加:

xml 复制代码
android:sharedUserId="android.uid.system"

和权限

xml 复制代码
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>

修改编译文件: vendor/apps/MyApplication/Android.mk

makefile 复制代码
#每个 Android.mk 文件必须以定义 LOCAL_PATH 为开始,它用于在开发树中查找源文件。  
LOCAL_PATH:= $(call my-dir)
#CLEAR_VARS 变量由 Build System 提供,并指向一个指定的 GNU Makefile,由它负责清理很多 LOCAL_xxx。
include $(CLEAR_VARS)
#模块名
LOCAL_MODULE := MyApplication
#apk 名  
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
#user、eng、tests、optional(所有版本都编译)
LOCAL_MODULE_TAGS := optional
#指定模块的类型,可不用定义 APPS、JAVA_LIBRAYIES、 SHARED_LIBRAYIES、 EXECUTABLES
LOCAL_MODULE_CLASS := APPS
#签名 platform 、PRESIGNED、 shared
LOCAL_CERTIFICATE := platform
# in system/app/
LOCAL_SYSTEM_MODULE := true
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
include $(BUILD_PREBUILT)

主要注意使用平台签名:LOCAL_CERTIFICATE := platform 和 将应用放到 system/app/LOCAL_SYSTEM_MODULE := true

然后删除之前编译的镜像文件out/target/product/emulator_x86_64/ 目录下搜索 .img 全部删除

然后执行四步走:

makefile 复制代码
//先初始化环境(每次打开虚拟机只需要执行一次环境即可)
source build/envsetup.sh

//选择上次编译过的机型(这个也是只选一次)
lunch sdk_phone_x86_64-eng

//开始整编(后续修改源码,只需要执行m就行,上面两个指令执行一次就行了)
m 

//启动模拟器
emulator -writable-system

编译完成,运行模拟器,打开预装的应用,在终端输入以下指令设置值和获取值:

makefile 复制代码
adb shell settings put system test_key 0
adb shell settings put system test_key 1
adb shell settings get system test_key

日志打印:

总结

  • SystemProperties 可用于获取和修改系统属性,但它是隐藏 API,需要特殊方法访问。
  • ContentObserver 用于监听 ContentProvider 数据变化,适用于监听系统设置的修改。
相关推荐
恋猫de小郭12 分钟前
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
android·开发语言·javascript·kotlin
flying robot2 小时前
小结:Android系统架构
android·系统架构
xiaogai_gai2 小时前
有效的聚水潭数据集成到MySQL案例
android·数据库·mysql
鹅鹅鹅呢3 小时前
mysql 登录报错:ERROR 1045(28000):Access denied for user ‘root‘@‘localhost‘ (using password Yes)
android·数据库·mysql
在人间负债^3 小时前
假装自己是个小白 ---- 重新认识MySQL
android·数据库·mysql
Unity官方开发者社区3 小时前
Android App View——团结引擎车机版实现安卓应用原生嵌入 3D 开发场景
android·3d·团结引擎1.5·团结引擎车机版
进击的CJR6 小时前
MySQL 8.0 OCP 英文题库解析(三)
android·mysql·开闭原则
Mckay8810 小时前
android studio导入项目
android·ide·android studio
工业互联网专业11 小时前
基于springboot+vue的医院门诊管理系统
java·vue.js·spring boot·毕业设计·源码·课程设计·医院门诊管理系统
是店小二呀11 小时前
【优选算法 | 字符串】字符串模拟题精选:思维+实现解析
android·c++·算法