Android 系统属性 prop 读写:自定义系统参数、固件配置参数持久保存

Android 系统属性 prop 读写:自定义系统参数、固件配置参数持久保存

工控设备开机要记住上次配置——IP 地址、波特率、传感器校准值。靠 SharedPreferences 不够硬核,系统属性 prop 才是固件级的"刻入DNA"方案。

一、Android 系统属性机制简介

1.1 prop 是什么

Android 系统属性(System Property)是一套全局的键值对(key-value)存储机制,运行在 native 层,由属性服务(Property Service)统一管理。它有以下几个特点:

  • 全局共享:所有进程都能读取,跨进程无需 IPC
  • 内存存储:属性值存在共享内存中,读取速度极快
  • 部分持久化:带persist.前缀的属性会写入文件,重启不丢失
  • 类型单一:只能存储字符串,数字也得存成字符串

简单理解:prop 就是一个系统级的 HashMap,所有 app 都能读,只有特定权限才能写。

1.2 persist 前缀的含义

属性名前缀决定了它的行为:

前缀持久化重启后写入权限
persist.✅ 写入/data/property/persistent_properties保留system/root
persist.sys.✅ 同上保留system/root
ro.❌ 只读属性保留(编译时写入)不可修改
sys.❌ 仅内存丢失system
vendor.❌ 仅内存丢失vendor
debug.❌ 仅内存丢失system
  • ro.开头:read-only,编译时定死,运行时不可修改(如ro.product.model
  • persist.开头:可修改且持久化,重启后值依然存在
  • 其他前缀:可修改但不持久化,重启后恢复默认

工控固件配置必须用persist.前缀,否则设备一断电配置就没了。

二、常用系统属性查看与修改

2.1 getprop 命令

通过 adb shell 查看系统属性:

# 查看所有属性getprop# 查看指定属性getprop ro.product.model getprop ro.build.version.release# 查看带前缀的所有属性getprop|greppersist.

常用属性一览:

属性名含义
ro.product.model设备型号
ro.build.version.releaseAndroid 版本号
ro.build.version.sdkSDK 版本号
ro.serialno设备序列号
ro.bootmode启动模式
persist.sys.locale系统语言
persist.sys.timezone时区
persist.sys.usb.configUSB 模式(adb/mtp)

2.2 setprop 命令

修改属性值(需要 root 或系统权限):

# 设置属性setprop persist.sys.locale zh-CN# 设置自定义工控属性setprop persist.serial.baudrate115200setprop persist.serial.device /dev/ttyS1# 删除持久化属性(设为空字符串即可)setprop persist.myconfig""

setprop在普通 app 进程中执行不了,需要 adb root 或在系统源码中通过property_set()调用。

三、Android 源码中属性定义

3.1 build.prop 与 system.prop

Android 编译时,属性来源有多个文件:

文件路径说明
build/tools/buildinfo.sh生成基础 build.prop
device/<vendor>/<device>/system.prop设备厂商自定义属性
system/property_contexts属性 SELinux 上下文定义
default.prop/prop.default编译时注入的默认属性

最终编译输出到设备上的/system/build.prop/vendor/build.prop/odm/build.prop

3.2 在源码中添加自定义属性

在设备目录下创建或编辑system.prop

# 工控板串口配置 persist.serial.baudrate=9600 persist.serial.databits=8 persist.serial.parity=none persist.serial.stopbits=1 # 网关配置 persist.gateway.server_ip=192.168.1.100 persist.gateway.server_port=8080 persist.gateway.protocol=tcp # 传感器校准 persist.sensor.temp_offset=0.0 persist.sensor.humi_offset=0.0

3.3 property_service 的写入限制

不是什么属性都能随便写的。属性服务在property_service.cpp中定义了属性写入权限白名单

// system/core/init/property_service.cppstaticconststruct{constchar*prefix;unsignedintuid;unsignedintgid;}property_perms[]={{"net.rmnet0.",AID_RADIO,0},{"net.gprs.",AID_RADIO,0},{"net.ppp.",AID_ROOT,0},{"persist.sys.",AID_SYSTEM,0},{"persist.service.",AID_SYSTEM,0},{"persist.",AID_SYSTEM,0},// persist.* 需要system权限{"sys.",AID_SYSTEM,0},// ...};

要点:

  • persist.*属性写入需要system UID(即系统应用)
  • 普通第三方 app 无法直接setprop
  • 需要将 app 签名为平台签名(platform.pk8+platform.x509.pem

四、APP 中读写系统属性:SystemProperties 反射调用

Android 隐藏了SystemProperties类(@hideAPI),普通 app 无法直接调用,但可以通过反射访问。

4.1 SystemProperties 反射封装

publicclassSystemPropUtils{privatestaticClass<?>sSystemPropertiesClass;privatestaticMethodsGetMethod;privatestaticMethodsSetMethod;privatestaticMethodsGetIntMethod;privatestaticMethodsGetBooleanMethod;static{try{sSystemPropertiesClass=Class.forName("android.os.SystemProperties");sGetMethod=sSystemPropertiesClass.getMethod("get",String.class);sSetMethod=sSystemPropertiesClass.getMethod("set",String.class,String.class);sGetIntMethod=sSystemPropertiesClass.getMethod("getInt",String.class,int.class);sGetBooleanMethod=sSystemPropertiesClass.getMethod("getBoolean",String.class,boolean.class);}catch(Exceptione){e.printStackTrace();}}/** * 读取字符串属性 */publicstaticStringget(Stringkey,Stringdef){try{return(String)sGetMethod.invoke(null,key);}catch(Exceptione){returndef;}}/** * 读取整型属性 */publicstaticintgetInt(Stringkey,intdef){try{return(int)sGetIntMethod.invoke(null,key,def);}catch(Exceptione){returndef;}}/** * 读取布尔属性 */publicstaticbooleangetBoolean(Stringkey,booleandef){try{return(boolean)sGetBooleanMethod.invoke(null,key,def);}catch(Exceptione){returndef;}}/** * 写入属性(需要system权限) */publicstaticvoidset(Stringkey,Stringvalue){try{sSetMethod.invoke(null,key,value);}catch(Exceptione){e.printStackTrace();}}}

4.2 工控固件配置管理完整示例

publicclassFirmwareConfigManager{// 属性Key定义publicstaticfinalStringKEY_SERIAL_BAUDRATE="persist.serial.baudrate";publicstaticfinalStringKEY_SERIAL_DEVICE="persist.serial.device";publicstaticfinalStringKEY_GATEWAY_IP="persist.gateway.server_ip";publicstaticfinalStringKEY_GATEWAY_PORT="persist.gateway.server_port";publicstaticfinalStringKEY_GATEWAY_PROTOCOL="persist.gateway.protocol";publicstaticfinalStringKEY_SENSOR_TEMP_OFFSET="persist.sensor.temp_offset";publicstaticfinalStringKEY_DEVICE_ID="persist.device.id";// 默认值privatestaticfinalintDEFAULT_BAUDRATE=9600;privatestaticfinalStringDEFAULT_SERIAL_DEVICE="/dev/ttyS1";privatestaticfinalStringDEFAULT_GATEWAY_IP="192.168.1.100";privatestaticfinalintDEFAULT_GATEWAY_PORT=8080;privatestaticfinalStringDEFAULT_PROTOCOL="tcp";privatestaticvolatileFirmwareConfigManagersInstance;privateFirmwareConfigManager(){}publicstaticFirmwareConfigManagergetInstance(){if(sInstance==null){synchronized(FirmwareConfigManager.class){if(sInstance==null){sInstance=newFirmwareConfigManager();}}}returnsInstance;}// === 串口配置 ===publicintgetSerialBaudrate(){returnSystemPropUtils.getInt(KEY_SERIAL_BAUDRATE,DEFAULT_BAUDRATE);}publicvoidsetSerialBaudrate(intbaudrate){SystemPropUtils.set(KEY_SERIAL_BAUDRATE,String.valueOf(baudrate));}publicStringgetSerialDevice(){returnSystemPropUtils.get(KEY_SERIAL_DEVICE,DEFAULT_SERIAL_DEVICE);}publicvoidsetSerialDevice(Stringdevice){SystemPropUtils.set(KEY_SERIAL_DEVICE,device);}// === 网关配置 ===publicStringgetGatewayIp(){returnSystemPropUtils.get(KEY_GATEWAY_IP,DEFAULT_GATEWAY_IP);}publicvoidsetGatewayIp(Stringip){SystemPropUtils.set(KEY_GATEWAY_IP,ip);}publicintgetGatewayPort(){returnSystemPropUtils.getInt(KEY_GATEWAY_PORT,DEFAULT_GATEWAY_PORT);}publicvoidsetGatewayPort(intport){SystemPropUtils.set(KEY_GATEWAY_PORT,String.valueOf(port));}// === 传感器校准 ===publicfloatgetTempOffset(){Stringval=SystemPropUtils.get(KEY_SENSOR_TEMP_OFFSET,"0.0");returnFloat.parseFloat(val);}publicvoidsetTempOffset(floatoffset){SystemPropUtils.set(KEY_SENSOR_TEMP_OFFSET,String.valueOf(offset));}// === 设备ID ===publicStringgetDeviceId(){returnSystemPropUtils.get(KEY_DEVICE_ID,"UNKNOWN");}publicvoidsetDeviceId(Stringid){SystemPropUtils.set(KEY_DEVICE_ID,id);}/** * 打印当前所有配置(调试用) */publicvoidprintAllConfig(){Log.i("FirmwareConfig","=== 工控固件配置 ===");Log.i("FirmwareConfig","串口设备: "+getSerialDevice());Log.i("FirmwareConfig","波特率: "+getSerialBaudrate());Log.i("FirmwareConfig","网关IP: "+getGatewayIp());Log.i("FirmwareConfig","网关端口: "+getGatewayPort());Log.i("FirmwareConfig","通信协议: "+SystemPropUtils.get(KEY_GATEWAY_PROTOCOL,DEFAULT_PROTOCOL));Log.i("FirmwareConfig","温度偏移: "+getTempOffset());Log.i("FirmwareConfig","设备ID: "+getDeviceId());}}

在 Activity 中使用:

publicclassConfigActivityextendsAppCompatActivity{privateFirmwareConfigManagermConfig;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_config);mConfig=FirmwareConfigManager.getInstance();mConfig.printAllConfig();// 读取配置填充UIetBaudrate.setText(String.valueOf(mConfig.getSerialBaudrate()));etGatewayIp.setText(mConfig.getGatewayIp());etGatewayPort.setText(String.valueOf(mConfig.getGatewayPort()));// 保存按钮btnSave.setOnClickListener(v->{mConfig.setSerialBaudrate(Integer.parseInt(etBaudrate.getText().toString()));mConfig.setGatewayIp(etGatewayIp.getText().toString());mConfig.setGatewayPort(Integer.parseInt(etGatewayPort.getText().toString()));Toast.makeText(this,"配置已保存(重启后生效)",Toast.LENGTH_SHORT).show();});}}

五、工控固件配置持久化方案对比

方案持久化跨进程权限要求适用场景
System Properties (persist.)✅ 全局system 签名固件级全局配置
SharedPreferences❌ 仅本appapp 内部配置
文件存储 (JSON/Properties)需自行处理文件权限大数据量配置
SQLite需自行处理结构化历史数据
Settings.GlobalWRITE_SETTINGS系统级简单配置

5.1 选择建议

  • 全局固件配置(串口参数、网关地址)→ 用persist.属性,native 层和 Java 层都能读
  • App 内部偏好(界面主题、提醒开关)→ 用 SharedPreferences
  • 大量历史数据(采集记录、报警日志)→ 用 SQLite 或文件
  • 需要 native 层读取的参数→ 必须用 prop,SharedPreferences 在 native 层读不了

prop 的最大优势是跨语言——C/C++ 层用property_get()读,Java 层用SystemProperties.get()读,Shell 用getprop读,三端共享同一份数据。这是其他方案做不到的。

六、常见工控 prop 属性汇总

属性名类型说明默认值
persist.serial.baudrateint串口波特率9600
persist.serial.devicestring串口设备节点/dev/ttyS1
persist.serial.paritystring校验方式none
persist.gateway.server_ipstring网关服务器IP192.168.1.100
persist.gateway.server_portint网关端口8080
persist.gateway.protocolstring通信协议tcp
persist.gateway.reconnect_intervalint重连间隔(ms)5000
persist.device.idstring设备唯一标识-
persist.device.locationstring设备安装位置-
persist.sensor.temp_offsetfloat温度校准偏移0.0
persist.sensor.humi_offsetfloat湿度校准偏移0.0
persist.gpio.relay_defaultint继电器默认状态0
persist.net.eth_ipstring以太网静态IP192.168.1.88
persist.net.eth_gatewaystring以太网网关192.168.1.1
persist.net.eth_netmaskstring以太网掩码255.255.255.0
persist.watchdog.enableboolean看门狗使能true
persist.watchdog.timeoutint看门狗超时(s)30

6.1 native 层读写属性(C/C++)

#include<cutils/properties.h>// 读取属性charvalue[92]={0};intret=property_get("persist.serial.baudrate",value,"9600");// ret > 0 表示读取成功,value 为属性值// 写入属性(需要对应权限)property_set("persist.serial.baudrate","115200");

在 native 服务进程中,直接调property_get/property_set即可读写同一份属性数据,与 Java 层完全同步。

6.2 通过 init.rc 设置默认值

init.rc中可以在启动时设置属性默认值:

on boot# 设置工控默认配置setprop persist.serial.baudrate9600setprop persist.serial.device /dev/ttyS1 setprop persist.gateway.reconnect_interval5000setprop persist.watchdog.enable1

注意:setprop只在属性不存在时设置默认值才有意义。已存在的persist.属性重启后不会覆盖,会保留上次的值。如果需要强制重置,可以在 init.rc 中先setprop persist.xxx ""再设默认值——但这会清除用户配置,慎用。

系统属性是 Android 工控开发中最高效的全局配置方案。掌握 prop 的定义、读写、持久化机制,就能实现固件级参数管理:native 层和 Java 层共享配置,断电不丢失,跨进程零成本读取。这在定制固件、设备量产、远程运维等场景中是不可替代的能力。