安卓源码学习之【系统属性与 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

在 Activity 或 Service 中注册 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 处理设置变化

在 SettingsObserver 的 onChange 方法中处理设置变化。例如,如果你只关心特定的设置项,可以根据 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 数据变化,适用于监听系统设置的修改。
相关推荐
mmsx1 小时前
Android 插件化架构:40 个 Gradle 模块的拆分、通信与共存
android·kotlin
mmsx1 小时前
Android 不改一行源码:用 AspectJ 做三类拦截
android·kotlin
TST-魚爷1 小时前
易视讯会议摄像头 + 全向麦克风实操讲解稿
android·电脑·钉钉·腾讯会议·zoom会议
嘟哩DuliDuli2 小时前
AI创作工作区的项目上下文与多模态任务边界
android·人工智能·安全·ai·软件工程
imDwAaY3 小时前
MySQL 存储引擎是什么?
android·数据库·mysql
Meteors.4 小时前
Android Compose学习02:基础组件与 Modifier
android·学习
其实防守也摸鱼5 小时前
存储管理:页面置换算法学习任务(测验题要求给出置换页面顺序,缺页次数,缺页率)
android·前端·数据库·学习·oracle·自动化
mmsx7 小时前
Android 地图上绘制 KML:解析、样式与渲染
android·xml
运行时异常7 小时前
【无标题】
android
右手吉他8 小时前
高通平台IA APN问题分析之一
android·物联网