仓颉 ArkUI Environment 设备环境查询:从系统环境变量到 UI 状态同步的完整指南
仓颉 ArkUI Environment 设备环境查询:从系统环境变量到 UI 状态同步的完整指南
Environment 是 ArkUI 框架在应用程序启动时创建的单例对象,用于向应用提供设备运行环境参数(如多语言、深浅色模式、字体缩放等)的查询能力。本文基于仓颉语料库中的官方文档与参考 API 文档,完整讲解 Environment 的内置参数、与 AppStorage 的协作机制、UI 内与应用逻辑中的两种典型使用方式,以及 fontScale 跟随系统的配置方法,帮助开发者基于设备环境参数实现自适应场景判断。
Environment 是什么:框架启动时创建的单例
Environment 是 ArkUI 框架在应用程序启动时创建的单例对象,为 AppStorage 提供了一系列描述应用程序运行状态的属性。从参考 API 文档 cj-state-rendering-appstatemanagement.md 可以看到其定义为:
public class Environment {}
其功能定位是:Environment 是和应用的进程绑定的,由 UI 框架在应用程序启动时创建,为应用程序提供设备环境状态的中心存储。系统能力要求为 SystemCapability.ArkUI.ArkUI.Full,起始版本为 API 22。
在仓颉状态管理 V1 的整体架构中,Environment 属于"管理应用拥有的状态"这一层级。正如 状态管理概述 所述,AppStorage 是应用状态的"中枢",PersistentStorage 和 Environment 都需要通过 AppStorage 中转,才可以和 UI 交互。因此,Environment 本身不直接面向组件提供数据,而是将设备环境变量的值写入 AppStorage,开发者通过 AppStorage 才能获取环境变量的值。
Environment 的属性具有两个关键特征:
- 不可变性:Environment 的所有属性对应用而言都是不可写的,应用只能查询,不能写入或修改。
- 简单类型:Environment 的所有属性都是简单类型(Bool、枚举、Float64、String 等),不涉及复杂对象。
说明:
阅读本文前,建议先阅读 AppStorage,理解应用全局 UI 状态存储的机制,以及 管理应用拥有的状态概述 中 Environment 的定位。
Environment 内置参数一览
Environment 预置了一系列与设备运行环境相关的键,开发者可直接通过键名查询。下表汇总了全部内置参数及其数据类型、含义(来自 cj-environment.md):
| 键 | 数据类型 | 描述 |
|---|---|---|
| accessibilityEnabled | Bool | 获取无障碍屏幕读取是否启用。 |
| colorMode | ColorMode | 色彩模型类型:选项为 ColorMode.Light(浅色)、ColorMode.Dark(深色)。 |
| fontScale | Float64 | 字体大小比例。开发者需要配置 configuration,设置 fontSizeScale 为 "followSystem",使 fontScale 跟随系统变化(具体配置步骤见下文"fontScale 跟随系统的配置")。初始值跟随系统默认参数。 |
| fontWeightScale | Float64 | 字体粗细程度。在不同的系统或者机型中,fontWeightScale 的取值范围可能会有所不同。初始值跟随系统默认参数。 |
| layoutDirection | LayoutDirection | 布局方向类型:包括 LayoutDirection.Ltr(从左到右)、LayoutDirection.Rtl(从右到左)。 |
| languageCode | String | 当前系统语言值,取值必须为小写字母(例如:zh)。初始值跟随系统默认参数。 |
其中 colorMode 与 layoutDirection 的枚举定义在参考 API 文档中均有详细说明:
- ColorMode:
public enum ColorMode <: Equatable<ColorMode> { | Light | Dark | ... },支持==与!=运算符比较,Light表示浅色模式,Dark表示深色模式。 - LayoutDirection:
public enum LayoutDirection <: Equatable<LayoutDirection> { | Ltr | Rtl | Auto | ... },Ltr表示从左到右布局,Rtl表示从右到左布局,Auto表示自动布局,同样支持相等/不等比较运算符。
数据流向与读写约束:Environment → AppStorage → Component
Environment 的核心价值在于打通"设备系统环境"与"应用 UI 状态"之间的数据链路,其更新链为:
Environment --> AppStorage --> Component
即:Environment 读取系统环境变量的值并写入 AppStorage;组件通过 AppStorage 提供的装饰器(如 @StorageProp)或 API 接口获取这些值。从 cj-appstorage.md 的"限制条件"小节可以印证一个重要约束:
如果在 AppStorage 中已经创建属性后,再调用 Environment.envProp() 创建同名的属性,会调用失败。因为 AppStorage 已经有同名属性,Environment 环境变量不会再写入 AppStorage 中,所以建议 AppStorage 中属性不要使用 Environment 预置环境变量名。
这条约束提醒开发者:不要在 AppStorage 中自行创建与 Environment 内置键同名的属性(如 languageCode、colorMode 等),否则 Environment 的环境变量将无法写入,查询结果可能与预期不符。
另一个重要约束是写回限制:@StorageProp 关联的环境参数可以在本地更改,但不能同步回 AppStorage 中。这是因为应用对环境变量参数是不可写的,Environment 只提供查询能力。
从 UI 中访问 Environment 参数
在自定义组件中访问环境参数,需要两步操作:
- 使用
Environment.envProp<T>(key, defaultValue)将设备运行的环境变量存入 AppStorage; - 在自定义组件中通过
@StorageProp["key"]获取该值并绑定到 UI。
典型代码示例如下(来自 cj-environment.md):
// 将设备的语言code存入AppStorage,默认值为en
Environment.envProp<String>("languageCode", "en")
@StorageProp["languageCode"] let languageCode: String = "en"
将两者结合到完整组件中,即构成一条完整的"设备环境 → UI"数据链:
package ohos_app_cangjie_entry
import kit.ArkUI.*
import ohos.arkui.state_macro_manage.*
@Entry
@Component
class EntryView {
let SysLanguageCode = Environment.envProp<String>("languageCode", "en")
@StorageProp["languageCode"]
let languageCode: String = "en"
func build() {
Row() {
Column() {
// 输出当前设备的languageCode
Text(this.languageCode)
}
}
}
}
这里涉及 @StorageProp 的核心语义(详见 cj-appstorage.md 的 @StorageProp 章节):
- @StorageProp(key) 与 AppStorage 中 key 对应的属性建立单向数据同步:AppStorage 中给定 key 的属性一旦发生变化,就会同步给 @StorageProp,并覆盖掉本地的修改。
- 被装饰变量必须指定初始值:如果 AppStorage 实例中不存在对应属性,则用该初始值初始化该属性,并存入 AppStorage 中。
- @StorageProp 不允许从父节点初始化和更新,只能由 AppStorage 中 key 对应的属性初始化。
在应用逻辑中使用 Environment
Environment 的数据不仅可以驱动 UI,也可以在应用业务逻辑(非 UI 渲染路径)中直接读取。此时不通过 @StorageProp,而是使用 AppStorage 的 API 接口获取单向绑定的属性对象。
package ohos_app_cangjie_entry
import kit.ArkUI.*
import ohos.arkui.state_macro_manage.*
import kit.PerformanceAnalysisKit.Hilog
@Entry
@Component
class EntryView {
// 使用Environment.EnvProp将设备运行languageCode存入AppStorage中;
let SysLanguageCode = Environment.envProp<String>("languageCode", "en")
// 从AppStorage获取单向绑定的languageCode的变量
let lang: ObservedProperty<String> = AppStorage.property<String>('languageCode').getOrThrow()
func build() {
Row() {
Column() {
Button("lang")
.onClick({
evt =>
if (lang.get()=='zh') {
Hilog.info(0, "Chinese", "你好")
} else {
Hilog.info(0, "En", "Hello")
}
})
}
}
}
}
这段示例演示了基于 languageCode 做多语言场景判断的典型写法:通过 AppStorage.property<String>('languageCode') 获取与 AppStorage 中 languageCode 属性关联的 ObservedProperty<String>,再通过 .getOrThrow() 取出当前值。当设备语言为 zh 时输出中文日志,否则输出英文日志,实现了"同一套代码、不同语言环境"的动态适配。
Environment 的 API 细节
Environment 提供以下静态 API(定义于 cj-state-rendering-appstatemanagement.md):
static func envProp<T>(String, T)
public static func envProp<T>(key: String, defaultValue: T): Bool
创建一个与设备环境状态同步的属性,其参数与返回值如下:
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| key | String | 是 | - | 环境属性的键名。 |
| defaultValue | T | 是 | - | 属性的默认值。 |
返回值类型为 Bool:如果创建成功则返回 true,否则返回 false。创建失败的一种典型场景即上文提到的"AppStorage 中已存在同名属性"。
static func keys()
public static func keys(): Array<String>
获取所有环境属性的键名,返回值为 Array<String>(所有环境属性的键名数组)。可用于运行时枚举当前 Environment 已注册的环境属性,便于调试或动态处理。
fontScale 跟随系统的配置
fontScale(字体大小比例)这一环境参数比较特殊:其值默认跟随系统默认参数,但如果希望应用字体大小随系统设置实时变化,需要在工程的配置文件中显式开启。具体配置步骤如下(参见 app-configuration-file.md):
- 在 app.json5 中通过
configuration标签引用 profile 资源:
{
"app": {
"configuration": "$profile:configuration"
}
}
- 在开发视图的
AppScope/resources/base/profile下定义配置文件configuration.json(文件名"configuration"可自定义,需要与configuration标签指定的信息对应),并在其中声明字体跟随系统变化的属性:
{
"configuration": {
"fontSizeScale": "followSystem",
"fontSizeMaxScale": "3.2"
}
}
其中两个关键属性说明如下:
- fontSizeScale:应用字体大小是否跟随系统。取值为
followSystem(跟随系统)或nonFollowSystem(不跟随系统),可缺省,缺省值为nonFollowSystem。 - fontSizeMaxScale:应用字体大小选择跟随系统后,相比系统字体的最大比例。支持的取值为
1、1.15、1.3、1.45、1.75、2、3.2,可缺省,缺省值为3.2。当fontSizeScale为nonFollowSystem时该项不生效。
官方文档给出的一个示例场景:配置最大比例为 1.75,系统字体默认大小为 10fp。如果用户在设置中把系统字体调整为 1.5 倍,此时系统实际字体大小为 15fp,应用会跟随系统字体一起调整为 15fp;而如果用户在设置中把系统字体调整为 2 倍,系统字体大小为 20fp,但由于应用配置的最大比例为 1.75,应用字体大小会封顶为 17.5fp。
完成上述配置后,应用读取到的 Environment 的 fontScale 值即可跟随系统设置动态变化,进而支撑"文本随系统字体缩放"的自适应 UI 需求。
常见问题与注意事项
综合 cj-environment.md 与 cj-appstorage.md 的内容,使用 Environment 时有以下要点需要留意:
- 属性不可写:应用对 Environment 环境变量参数是不可写的,只能在 Environment 中查询;@StorageProp 本地对值的更改不会同步回 AppStorage。
- 避免同名冲突:AppStorage 中不要使用 Environment 预置环境变量名(如
languageCode、colorMode等)创建属性,否则Environment.envProp()会调用失败。 - @StorageProp 的宏参数要求:key 必须是带引号的常量字符串,类型必须显式指定;@StorageProp 与 @StorageLink 不支持装饰 Func 类型变量(框架会抛出运行时错误),也不支持 Any 类型。
- 数据必须经 AppStorage 中转:Environment 本身不与 UI 直接交互,开发者需通过 AppStorage 的装饰器或 API 获取环境变量值,这与 PersistentStorage 的机制一致。
- 状态管理仅限 UI 主线程:当前状态管理功能仅支持在 UI 主线程使用,不能在子线程、worker、taskpool 中使用(见 状态管理概述),Environment 相关操作同样遵循该限制。
综上,Environment 为仓颉应用提供了一套简洁、只读的设备环境查询入口,配合 AppStorage 的单向/双向同步机制,可以在多语言、深浅色模式、布局方向、无障碍与字体缩放等典型场景中实现"一次开发、随环境自适应"的效果。