SystemProperties属性变化监听 - 需要设置属性的地方主动触发

文章详细介绍了Android中SystemProperties属性的设置和获取方法,包括通过adb、C++以及JavaAPI的使用。同时,讨论了属性设置改变的底层实现,主要是通过__system_property_set()进行socket通信。在属性变化监听方面,指出只有在SystemProperties加载时有属性变化通知,主要应用于rc文件的监听。Java中可以通过addChangeCallback添加监听回调,但C++中目前没有内置的监听机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

android-12.0.0_r3


1、prop属性设置和获取

  • adb
    adb shell getprop <key>
    adb shell setprop <key> <value>
  • C++
    /system/core/libcutils/properties.cpp
    int property_get(const char* key, char* value, const char* default_value)
    int property_set(const char* key, const char* value)
  • java
    /frameworks/base/core/java/android/os/SystemProperties.java
    public static String get(@NonNull String key)
    public static void set(@NonNull String key, @Nullable String val)

在这里插入图片描述在这里插入图片描述

2、prop属性设置改变

实质都是__system_property_set() 调用writev进行socket通信设置

2.1 adb执行文件设置

  • 代码位置/system/core/toolbox/getprop.cpp,编译生成执行文件system/bin/getprop

adb shell ls /system/bin | findstr prop
在这里插入图片描述

  • 最终调用SetProperty__system_property_set() 调用writev进行socket通信设置

/system/libbase/properties.cpp
/bionic/libc/include/sys/system_properties.h
/bionic/libc/bionic/system_property_set.cpp

在这里插入图片描述
在这里插入图片描述

2.2 C++中设置

  • 代码位置/system/core/libcutils/properties.cpp
  • 最终调用,同上 2.1 adb执行文件设置

/system/libbase/properties.cpp
/bionic/libc/bionic/system_property_set.cpp

248  int __system_property_set(const char* key, const char* value) {
249    if (key == nullptr) return -1;
250    if (value == nullptr) value = "";
251  
252    if (g_propservice_protocol_version == 0) {
253      detect_protocol_version();
254    }
255  
256    if (g_propservice_protocol_version == kProtocolVersion1) {
257      // Old protocol does not support long names or values
258      if (strlen(key) >= PROP_NAME_MAX) return -1;
259      if (strlen(value) >= PROP_VALUE_MAX) return -1;
260  
261      prop_msg msg;
262      memset(&msg, 0, sizeof msg);
263      msg.cmd = PROP_MSG_SETPROP;
264      strlcpy(msg.name, key, sizeof msg.name);
265      strlcpy(msg.value, value, sizeof msg.value);
266  
267      return send_prop_msg(&msg);
268    } else {
269      // New protocol only allows long values for ro. properties only.
270      if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
271      // Use proper protocol
272      PropertyServiceConnection connection;
273      if (!connection.IsValid()) {
274        errno = connection.GetLastError();
275        async_safe_format_log(
276            ANDROID_LOG_WARN, "libc",
277            "Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
278            errno, strerror(errno));
279        return -1;
280      }
281  
282      SocketWriter writer(&connection);
283      if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
284        errno = connection.GetLastError();
285        async_safe_format_log(ANDROID_LOG_WARN, "libc",
286                              "Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
287                              key, value, errno, strerror(errno));
288        return -1;
289      }
290  
291      int result = -1;
292      if (!connection.RecvInt32(&result)) {
293        errno = connection.GetLastError();
294        async_safe_format_log(ANDROID_LOG_WARN, "libc",
295                              "Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
296                              key, value, errno, strerror(errno));
297        return -1;
298      }
299  
300      if (result != PROP_SUCCESS) {
301        async_safe_format_log(ANDROID_LOG_WARN, "libc",
302                              "Unable to set property \"%s\" to \"%s\": error code: 0x%x", key, value,
303                              result);
304        return -1;
305      }
306  
307      return 0;
308    }
309  }

2.3 java中设置

  • 代码位置/frameworks/base/core/java/android/os/SystemProperties.java
  • JNI最终调用,同上 2.1 adb执行文件设置

/frameworks/base/core/jni/android_os_SystemProperties.cpp
/bionic/libc/include/sys/system_properties.h
/bionic/libc/bionic/system_property_set.cpp

在这里插入图片描述

2.4 扩充"SystemProperties属性加载"设置

SystemProperties属性加载

3、prop属性变化监听

通过上面设置查看,只有 SystemProperties属性加载 设置时有属性设置变化通知PropertyChanged(name, value)

3.1、SystemProperties属性加载时属性变化通知

主要用于rc文件监听prop属性变化,ActionManager::GetInstance().QueuePropertyChange(name, value)通知,如/system/core/rootdir/init.rcon property:sys.boot_completed=1监听开机完成处理相关操作

/system/core/init/property_service.cpp
/system/core/init/init.cpp

329  void PropertyChanged(const std::string& name, const std::string& value) {
330      // If the property is sys.powerctl, we bypass the event queue and immediately handle it.
331      // This is to ensure that init will always and immediately shutdown/reboot, regardless of
332      // if there are other pending events to process or if init is waiting on an exec service or
333      // waiting on a property.
334      // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
335      // commands to be executed.
336      if (name == "sys.powerctl") {
337          trigger_shutdown(value);
338      }
339  
340      if (property_triggers_enabled) {
341          ActionManager::GetInstance().QueuePropertyChange(name, value);
342          WakeMainInitThread();
343      }
344  
345      prop_waiter_state.CheckAndResetWait(name, value);
346  }

3.2、C++中设置属性变化没有监听

需要去实现,并要考虑通知到上层java

3.3、java中设置属性监听变化 SystemProperties.java

236      /**
237       * Add a callback that will be run whenever any system property changes.
238       *
239       * @param callback The {@link Runnable} that should be executed when a system property
240       * changes.
241       * @hide
242       */
243      @UnsupportedAppUsage
244      public static void addChangeCallback(@NonNull Runnable callback) {
245          synchronized (sChangeCallbacks) {
246              if (sChangeCallbacks.size() == 0) {
247                  native_add_change_callback();
248              }
249              sChangeCallbacks.add(callback);
250          }
251      }
252  
253      /**
254       * Remove the target callback.
255       *
256       * @param callback The {@link Runnable} that should be removed.
257       * @hide
258       */
259      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
260      public static void removeChangeCallback(@NonNull Runnable callback) {
261          synchronized (sChangeCallbacks) {
262              if (sChangeCallbacks.contains(callback)) {
263                  sChangeCallbacks.remove(callback);
264              }
265          }
266      }
267  
268      @SuppressWarnings("unused")  // Called from native code.
269      private static void callChangeCallbacks() {
270          ArrayList<Runnable> callbacks = null;
271          synchronized (sChangeCallbacks) {
272              //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
273              if (sChangeCallbacks.size() == 0) {
274                  return;
275              }
276              callbacks = new ArrayList<Runnable>(sChangeCallbacks);
277          }
278          final long token = Binder.clearCallingIdentity();
279          try {
280              for (int i = 0; i < callbacks.size(); i++) {
281                  try {
282                      callbacks.get(i).run();
283                  } catch (Throwable t) {
284                      // Ignore and try to go on. Don't use wtf here: that
285                      // will cause the process to exit on some builds and break tests.
286                      Log.e(TAG, "Exception in SystemProperties change callback", t);
287                  }
288              }
289          } finally {
290              Binder.restoreCallingIdentity(token);
291          }
292      }
293  
294      /**
295       * Notifies listeners that a system property has changed
296       * @hide
297       */
298      @UnsupportedAppUsage
299      public static void reportSyspropChanged() {
300          native_report_sysprop_change();
301      }

3.3.1 addChangeCallback添加监听

  • native_add_change_callback() JNI注册到底层
  • sChangeCallbacks.add(callback)添加监听
/**
 * Add a callback that will be run whenever any system property changes.
 *
 * @param callback The {@link Runnable} that should be executed when a system property
 * changes.
 * @hide
 */
@UnsupportedAppUsage
public static void addChangeCallback(@NonNull Runnable callback) {
    synchronized (sChangeCallbacks) {
        if (sChangeCallbacks.size() == 0) {
            native_add_change_callback();
        }
        sChangeCallbacks.add(callback);
    }
}

3.3.2 callChangeCallbacks()

misc.cpp#report_sysprop_change() -> android_os_SystemProperties.cpp#do_report_sysprop_change() -> SystemProperties.java#callChangeCallbacks()
从流程上查看需要主动触发变化上报,但是查看最终__system_property_set()设置属性地方没有触发,反而需要设置属性的地方主动触发

@SuppressWarnings("unused")  // Called from native code.
private static void callChangeCallbacks() {
    ArrayList<Runnable> callbacks = null;
    synchronized (sChangeCallbacks) {
        //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
        if (sChangeCallbacks.size() == 0) {
            return;
        }
        callbacks = new ArrayList<Runnable>(sChangeCallbacks);
    }
    final long token = Binder.clearCallingIdentity();
    try {
        for (int i = 0; i < callbacks.size(); i++) {
            try {
                callbacks.get(i).run();
            } catch (Throwable t) {
                // Ignore and try to go on. Don't use wtf here: that
                // will cause the process to exit on some builds and break tests.
                Log.e(TAG, "Exception in SystemProperties change callback", t);
            }
        }
    } finally {
        Binder.restoreCallingIdentity(token);
    }
}

3.3.3 简易时序图

在这里插入图片描述

3.3.4 代码示例

参照时序图

/frameworks/base/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java

SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_CLEAR);
SystemPropPoker.getInstance().poke();

/frameworks/base/core/java/android/os/SystemService.java

static {
    SystemProperties.addChangeCallback(new Runnable() {
        @Override
        public void run() {
            synchronized (sPropertyLock) {
                sPropertyLock.notifyAll();
            }
        }
    });
}

/frameworks/base/core/java/android/view/WindowManagerGlobal.java
在这里插入图片描述

Spring Boot可以通过使用MySQL的binlog来实现实时监听某张表的变化。下面是一个基本的示例代码: 1. 在`application.properties`文件中添加以下配置: ``` spring.datasource.url=jdbc:mysql://localhost:3306/mydatabase?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8 spring.datasource.username=root spring.datasource.password=123456 spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect spring.jpa.show-sql=true spring.jpa.open-in-view=false spring.jpa.properties.hibernate.default_schema=mydatabase spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true spring.jpa.properties.hibernate.event.merge.entity_copy_observer=allow ``` 这里的`mydatabase`是你的数据库名称,`root`和`123456`是你的数据库用户名和密码。 2. 创建一个`DatabaseEventListener`类来监听数据库变化: ```java import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Component public class DatabaseEventListener { @PersistenceContext private EntityManager entityManager; @EventListener @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void handleDatabaseEvent(DatabaseEvent event) { // 处理数据库变化事件 System.out.println("表变化事件:" + event); } } ``` 这里的`DatabaseEvent`是自定义的事件类,你可以根据自己的需求定义自己的事件类。 3. 创建一个`DatabaseEvent`类来表示数据库变化事件: ```java public class DatabaseEvent { private String tableName; public DatabaseEvent(String tableName) { this.tableName = tableName; } public String getTableName() { return tableName; } public void setTableName(String tableName) { this.tableName = tableName; } @Override public String toString() { return "DatabaseEvent{" + "tableName='" + tableName + '\'' + '}'; } } ``` 4. 在需要监听的方法中,手动触发事件: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class ExampleService { @Autowired private ApplicationEventPublisher eventPublisher; @Transactional public void doSomethingAndTriggerEvent() { // 执行一些操作 // ... // 触发事件 eventPublisher.publishEvent(new DatabaseEvent("my_table")); } } ``` 这里的`my_table`是你需要监听的表名。 以上就是一个基本的示例,你可以根据自己的需求进行修改和扩展。注意,需要确保MySQL的binlog功能已经开启。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xhBruce

佛系随缘,共同探讨

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

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

打赏作者

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

抵扣说明:

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

余额充值