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

深入Android 12源码:SystemProperties.set()之后,你的监听回调为什么没执行?

深入Android 12源码:SystemProperties.set()之后,你的监听回调为什么没执行?

在Android开发中,系统属性(SystemProperties)的监听机制一直是开发者们津津乐道的话题。许多开发者都曾遇到过这样的困惑:明明已经调用了SystemProperties.set()方法设置了属性值,并且也通过addChangeCallback()注册了监听回调,但回调函数却迟迟没有执行。这背后究竟隐藏着怎样的机制?本文将带你深入Android 12源码,一探究竟。

1. SystemProperties的基本使用与监听机制

1.1 系统属性的设置与获取

在Android中,系统属性是一种跨进程的键值对存储机制,广泛应用于系统配置、状态传递等场景。开发者可以通过以下方式操作系统属性:

  • Java层

    // 设置属性 SystemProperties.set("persist.debug.enable", "1"); // 获取属性 String value = SystemProperties.get("persist.debug.enable");
  • C++层

    // 设置属性 property_set("persist.debug.enable", "1"); // 获取属性 char value[PROP_VALUE_MAX]; property_get("persist.debug.enable", value, "0");
  • ADB命令

    adb shell setprop persist.debug.enable 1 adb shell getprop persist.debug.enable

1.2 属性变化的监听机制

Android提供了addChangeCallback()方法用于注册属性变化的监听回调:

SystemProperties.addChangeCallback(new Runnable() { @Override public void run() { Log.d(TAG, "System property changed!"); } });

然而,很多开发者发现,即使属性值被成功修改,这个回调也不会自动触发。这引出了本文的核心问题:为什么监听回调没有执行?

2. 源码追踪:从Java到Native

2.1 Java层的SystemProperties

frameworks/base/core/java/android/os/SystemProperties.java中,我们可以看到set()方法的实现:

public static void set(@NonNull String key, @Nullable String val) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } if (val != null && val.length() > PROP_VALUE_MAX) { throw new IllegalArgumentException("val.length > " + PROP_VALUE_MAX); } native_set(key, val); }

关键点在于native_set()是一个JNI方法,它将调用转发到Native层。

2.2 JNI层的实现

frameworks/base/core/jni/android_os_SystemProperties.cpp中,我们可以看到JNI的实现:

static void android_os_SystemProperties_set(JNIEnv* env, jobject clazz, jstring keyJ, jstring valJ) { const char* key = env->GetStringUTFChars(keyJ, NULL); const char* val = env->GetStringUTFChars(valJ, NULL); int err = __system_property_set(key, val); env->ReleaseStringUTFChars(keyJ, key); env->ReleaseStringUTFChars(valJ, val); if (err < 0) { jniThrowException(env, "java/lang/IllegalArgumentException", "property_set failed"); } }

这里的关键是__system_property_set()函数,它负责实际的属性设置工作。

2.3 Native层的属性设置

bionic/libc/bionic/system_property_set.cpp中,__system_property_set()的实现揭示了属性设置的本质:

int __system_property_set(const char* key, const char* value) { // 参数检查... if (g_propservice_protocol_version == kProtocolVersion1) { // 旧协议处理... } else { // 新协议处理 PropertyServiceConnection connection; if (!connection.IsValid()) { return -1; } SocketWriter writer(&connection); if (!writer.WriteUint32(PROP_MSG_SETPROP2) .WriteString(key) .WriteString(value) .Send()) { return -1; } int result = -1; if (!connection.RecvInt32(&result)) { return -1; } return result; } }

可以看到,属性设置实际上是通过socket通信完成的,这与我们常见的IPC机制类似。

3. 监听回调的触发机制

3.1 监听回调的注册过程

当我们调用addChangeCallback()时,发生了什么?

public static void addChangeCallback(@NonNull Runnable callback) { synchronized (sChangeCallbacks) { if (sChangeCallbacks.size() == 0) { native_add_change_callback(); } sChangeCallbacks.add(callback); } }

native_add_change_callback()会向Native层注册一个全局的回调监听器。

3.2 回调触发的关键:reportSyspropChanged

在Android系统中,属性变化的通知并不是自动触发的。查看SystemProperties.java,我们发现了一个关键方法:

/** * Notifies listeners that a system property has changed * @hide */ public static void reportSyspropChanged() { native_report_sysprop_change(); }

这个方法会显式地通知所有监听器属性发生了变化。那么,谁负责调用这个方法呢?

3.3 属性变化通知的解耦设计

Android系统采用了"设置属性"和"通知变化"解耦的设计:

  1. 属性设置:通过__system_property_set()完成
  2. 变化通知:需要显式调用reportSyspropChanged()或类似机制

这种设计带来了几个优势:

  • 灵活性:可以根据需要决定是否通知变化
  • 性能:避免不必要的通知开销
  • 可控性:可以批量操作后统一通知

4. 实际应用中的解决方案

4.1 系统内部的实现模式

在Android系统代码中,我们可以看到几种典型的实现模式:

  1. SystemPropPoker模式

    SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_CLEAR); SystemPropPoker.getInstance().poke();
  2. 直接调用reportSyspropChanged

    SystemProperties.set("debug.trace", "1"); SystemProperties.reportSyspropChanged();
  3. init进程中的PropertyChanged: 在init进程中,属性变化会自动触发PropertyChanged事件:

    void PropertyChanged(const std::string& name, const std::string& value) { if (property_triggers_enabled) { ActionManager::GetInstance().QueuePropertyChange(name, value); WakeMainInitThread(); } }

4.2 开发者可用的解决方案

对于应用开发者,可以考虑以下几种方案:

  1. 使用SystemPropPoker(如果有权限):

    SystemProperties.set("my.custom.property", "value"); SystemPropPoker.getInstance().poke();
  2. 实现自己的通知机制

    public class PropertyHelper { private static final List<Runnable> sCallbacks = new ArrayList<>(); public static void setPropertyAndNotify(String key, String value) { SystemProperties.set(key, value); notifyCallbacks(); } private static void notifyCallbacks() { for (Runnable callback : sCallbacks) { callback.run(); } } public static void addCallback(Runnable callback) { sCallbacks.add(callback); } }
  3. 结合ContentObserver: 对于需要频繁监听的场景,可以考虑将属性值存储在SettingsProvider中,然后使用ContentObserver监听变化。

4.3 性能与注意事项

在使用属性监听时,需要注意以下几点:

  • 性能影响:频繁的属性变化和通知会影响系统性能
  • 线程安全:回调方法会在Binder线程中执行,需要注意线程安全问题
  • 权限限制:某些属性需要特定权限才能设置或监听

下表总结了不同场景下的推荐做法:

使用场景推荐方案注意事项
系统应用SystemPropPoker需要平台签名
普通应用自定义通知机制无法监听系统属性变化
系统服务reportSyspropChanged需要系统权限

5. 深入理解设计哲学

Android的这种设计体现了几个重要的系统设计原则:

  1. 关注点分离:将属性存储与变化通知分离,提高模块化程度
  2. 最小权限原则:避免不必要的广播和通知,提高安全性
  3. 性能优化:减少不必要的IPC通信,提高系统效率

在实际开发中,理解这些设计哲学有助于我们更好地使用系统API,并设计出更合理的应用架构。

http://www.jsqmd.com/news/821322/

相关文章:

  • PyTorch实战:如何正确保存训练检查点(checkpoint)以实现断点续训和模型部署
  • 论文答辩 PPT 卡壳?Paperxie AI 一键打通你的毕业 “最后一公里”
  • ARM TCM架构与CP15寄存器配置实战指南
  • MAX31856选型与避坑指南:8种热电偶、±45V保护、故障检测到底怎么用?
  • 化工厂防爆气象站核心功能全解析
  • 基于Kubernetes与GitOps构建生产级家庭实验室:从IaC到自动化运维
  • AIGC实战学习路线:从入门到精通的系统化教程资源导航
  • 基于YOLOv8的苹果叶片病害检测系统
  • ByteRover CLI:字节跳动内部开发提效工具的设计与实践
  • python:linux上matplotlib找不到手动添加的字体
  • AWR1843 CCS开发模式:从工程导入到算法调试全流程解析
  • ArcGIS栅格计算器还能这么玩?一个‘土办法’搞定土壤侵蚀分级(附替代Con函数的数值映射技巧)
  • TreeViewer:轻松创建专业级系统发育树可视化图表
  • DINOv2终极指南:如何选择最适合你的计算机视觉预训练模型
  • 如何在3分钟内为Windows 11 LTSC系统恢复微软商店功能:完整组件恢复指南
  • 从零打造 APP Inventor 蓝牙遥控核心:一个模板解锁多种硬件交互场景
  • RT-Thread Sensor框架下,5分钟搞定INA226电流电压功率监测(含I2C避坑指南)
  • ARINC429测试工具的技术演进与ANET429-x系统解析
  • 终极指南:5分钟搞定微信网页版访问限制,让微信在浏览器中流畅使用
  • 观察Taotoken按Token计费模式下的月度成本变化
  • 别让答辩 PPT 拖垮你的毕业季!PaperXie AI 一键生成答辩神器,小白也能零失误通关
  • 2026新疆旅拍店铺推荐:这5家工作室排名口碑双赢 - 速递信息
  • 别再只盯着YOLO了!回顾R-CNN:理解两阶段检测的基石与那些被遗忘的设计细节
  • 百度文库文档纯净打印工具:轻松获取无干扰阅读体验
  • Adafruit nRF52 BSP安装与BLE开发实战指南
  • 如何快速配置游戏插件加载器:终极DLL代理解决方案
  • 3步搞定暗黑破坏神2角色存档编辑:Diablo Edit2终极指南
  • DLSS Swapper:游戏性能优化新选择,一键管理DLSS版本
  • 从ALPS电位器到DSP:音频音量控制技术简史与DIY数字替代方案
  • 基于本地文档的智能问答系统:从向量检索到私有化部署