在这里插入图片描述

每日一句正能量

这个世上最温暖的成全莫过于让别人的欢喜落地有声。
当别人告诉你一件开心的事,你不只是说“挺好的”,而是真正看见、听见、回应那份喜悦。“落地有声”意味着那份欢喜没有被忽略、被敷衍,而是被接住并放大了。
人生不是单行道,也不是完美实验,而是一场可以不断调整节奏、换角度、换方向、允许失误的旅途。

导读

本文以“打开客厅灯”为主线,演示如何借助 HarmonyOS 6.1 Intents Kit,将传统的多级页面操作压缩为“语音或搜索一句话直达设备控制”,并给出架构设计、意图建模、ArkTS 代码、风险控制、异常处理与测试思路。

七个关键词

HarmonyOS 6.1、Intents Kit、智能家居、语音直达、全局搜索、ArkTS、设备控制


一、为什么智能家居需要“一步直达”

传统智能家居应用的典型操作链路是:

  1. 解锁手机;
  2. 找到并打开家庭应用;
  3. 进入“我的家”;
  4. 选择房间;
  5. 找到目标设备;
  6. 点击开关或进入控制面板;
  7. 等待设备状态刷新。

这条链路在首次使用时并不算复杂,但在“每天重复几十次”的高频场景中,任何一次多余点击都会被放大。更麻烦的是,用户往往并不关心应用的信息架构,他真正想完成的是一个业务动作,例如:

  • 打开客厅灯;
  • 把卧室空调调到 26 ℃;
  • 关闭书房窗帘;
  • 查询空气净化器滤芯寿命;
  • 启动“回家”场景。

用户表达的是目标,而不是“打开哪个页面”。因此,一步直达的核心不是再加一个 DeepLink,而是让系统理解“动作、对象、位置和参数”,再把结构化意图交给应用执行。

HarmonyOS 的 Intents Kit 面向的正是这类问题:应用将可执行的业务能力声明为系统可理解的意图,系统入口可通过语音、搜索等方式识别用户需求,再把匹配后的意图与参数分发给应用。这样,应用从“等待用户逐层寻找功能”转变为“直接承接用户目标”。

说明:不同 SDK 版本的装饰器、配置文件字段和发布流程可能调整,本文代码以工程化设计与 ArkTS 实现思路为主。接入实际项目时,应以当前 HarmonyOS 6.1 SDK、DevEco Studio 模板和官方 Intents Kit 文档为准。


二、目标交互:从六步压缩到一步

在这里插入图片描述

以“打开客厅灯”为例,目标交互如下:

2.1 语音入口

用户说:

“小艺,打开客厅灯。”

系统完成意图识别后,将结构化参数传给应用:

intentName = controlDevice
room       = 客厅
device     = 灯
action     = ON

应用不再打开首页,而是直接调用设备控制服务。控制成功后,系统返回:

“客厅灯已打开。”

2.2 搜索入口

用户在系统搜索中输入:

打开客厅灯

搜索结果直接展示“打开客厅灯”的服务结果卡。点击后可立即执行,或在高风险场景中先进行确认。

2.3 失败时也要一步到位

“一步直达”并不等于“一律静默执行”。系统必须区分以下情况:

  • 设备离线:明确提示设备当前不可用;
  • 设备重名:展示候选设备;
  • 权限失效:引导重新授权;
  • 高风险动作:要求二次确认;
  • 网络超时:允许重试,并避免重复执行;
  • 状态不确定:提示“指令已发送,设备状态待确认”。

三、整体架构设计

在这里插入图片描述

本文把方案拆成五层。

3.1 系统入口层

负责承接用户表达,包括:

  • 小艺语音;
  • 系统全局搜索;
  • 服务卡片;
  • 应用内搜索;
  • 快捷指令。

入口层只负责采集用户表达,不直接控制设备。

3.2 意图框架层

负责把自然语言转换为结构化意图:

  • 识别意图名称;
  • 抽取房间、设备、动作和数值;
  • 处理同义词;
  • 生成标准化参数;
  • 选择适合的结果呈现方式。

例如,“开一下客厅的灯”和“把客厅主灯打开”最终都应归一化为同一个动作。

3.3 应用编排层

这是应用侧最关键的一层,包括:

  • IntentDispatcher:接收并分发意图;
  • SceneResolver:解析设备、房间和家庭;
  • RiskPolicy:判断是否需要二次确认;
  • ResultPresenter:统一生成语音、搜索卡片和应用页面结果。

3.4 设备领域层

负责业务规则,而不是界面跳转:

  • 读取设备列表;
  • 解析设备唯一标识;
  • 检查在线状态;
  • 下发控制指令;
  • 刷新状态缓存;
  • 写入审计日志;
  • 做幂等保护。

3.5 设备接入层

屏蔽不同设备协议差异,可接入:

  • 家庭中枢;
  • 云端 IoT 平台;
  • 局域网设备;
  • 蓝牙设备;
  • 测试环境中的模拟设备。

四、意图模型设计

在这里插入图片描述

一个可维护的设备控制意图,至少应包含以下字段:

export enum DeviceAction {
  ON = 'ON',
  OFF = 'OFF',
  SET_LEVEL = 'SET_LEVEL',
  SET_TEMPERATURE = 'SET_TEMPERATURE',
  OPEN = 'OPEN',
  CLOSE = 'CLOSE'
}

export interface DeviceIntent {
  intentName: string;
  homeId?: string;
  room?: string;
  device?: string;
  deviceId?: string;
  action: DeviceAction;
  value?: number;
  requestId: string;
  source: 'VOICE' | 'SEARCH' | 'CARD' | 'APP';
}

4.1 为什么要保留 requestId

语音入口或搜索结果可能因为网络重试而重复触发。若“打开灯”重复执行通常没有严重后果,但“打开门锁”“启动烤箱”等动作不能依赖运气。requestId 用于:

  • 幂等校验;
  • 链路追踪;
  • 故障排查;
  • 审计记录;
  • 重试去重。

4.2 为什么不能只传设备名称

家庭中可能同时存在:

  • 客厅主灯;
  • 客厅灯带;
  • 客厅落地灯;
  • 两个都被用户简称为“灯”的设备。

因此,设备解析应遵循以下优先级:

  1. 明确的 deviceId
  2. 家庭 + 房间 + 设备名;
  3. 最近使用设备;
  4. 唯一模糊匹配;
  5. 返回候选列表让用户确认。

五、意图配置示例

不同 SDK 版本的意图声明方式可能存在差异。下面使用接近工程配置的 JSON5 形式说明字段设计,重点是语义建模方法

{
  "intents": [
    {
      "name": "controlDevice",
      "description": "控制家庭中的灯、空调、窗帘等设备",
      "parameters": [
        {
          "name": "home",
          "type": "string",
          "required": false
        },
        {
          "name": "room",
          "type": "string",
          "required": false
        },
        {
          "name": "device",
          "type": "string",
          "required": true
        },
        {
          "name": "action",
          "type": "string",
          "required": true,
          "enum": [
            "ON",
            "OFF",
            "OPEN",
            "CLOSE",
            "SET_LEVEL",
            "SET_TEMPERATURE"
          ]
        },
        {
          "name": "value",
          "type": "number",
          "required": false
        }
      ],
      "examples": [
        "打开客厅灯",
        "关闭卧室空调",
        "把书房窗帘打开",
        "把客厅灯调到百分之五十",
        "把卧室空调调到二十六度"
      ]
    }
  ]
}

5.1 同义词归一化

自然语言中的动作表达非常丰富:

const ACTION_ALIASES: Record<string, DeviceAction> = {
  '打开': DeviceAction.ON,
  '开启': DeviceAction.ON,
  '开一下': DeviceAction.ON,
  '关掉': DeviceAction.OFF,
  '关闭': DeviceAction.OFF,
  '调亮': DeviceAction.SET_LEVEL,
  '调暗': DeviceAction.SET_LEVEL,
  '升温': DeviceAction.SET_TEMPERATURE,
  '降温': DeviceAction.SET_TEMPERATURE
};

对于窗帘,“打开”应该映射为 OPEN;对于灯,“打开”应该映射为 ON。所以归一化不能只看动词,还要结合设备类型。


六、ArkTS 核心实现

6.1 统一结果模型

export enum CommandStatus {
  SUCCESS = 'SUCCESS',
  NEED_CONFIRM = 'NEED_CONFIRM',
  NEED_SELECT = 'NEED_SELECT',
  OFFLINE = 'OFFLINE',
  FORBIDDEN = 'FORBIDDEN',
  TIMEOUT = 'TIMEOUT',
  FAILED = 'FAILED'
}

export interface DeviceCandidate {
  id: string;
  name: string;
  roomName: string;
  online: boolean;
}

export interface CommandResult {
  status: CommandStatus;
  message: string;
  deviceId?: string;
  deviceName?: string;
  candidates?: DeviceCandidate[];
  retryable?: boolean;
}

6.2 设备仓库

export interface SmartDevice {
  id: string;
  homeId: string;
  roomName: string;
  name: string;
  type: 'LIGHT' | 'AIR_CONDITIONER' | 'CURTAIN' | 'LOCK';
  online: boolean;
  ownerUserId: string;
}

export class DeviceRepository {
  private devices: SmartDevice[] = [
    {
      id: 'light-living-main',
      homeId: 'home-001',
      roomName: '客厅',
      name: '主灯',
      type: 'LIGHT',
      online: true,
      ownerUserId: 'user-001'
    },
    {
      id: 'curtain-study',
      homeId: 'home-001',
      roomName: '书房',
      name: '窗帘',
      type: 'CURTAIN',
      online: true,
      ownerUserId: 'user-001'
    }
  ];

  async findCandidates(
    userId: string,
    homeId: string | undefined,
    room: string | undefined,
    deviceKeyword: string
  ): Promise<SmartDevice[]> {
    const keyword = deviceKeyword.trim();

    return this.devices.filter((item: SmartDevice) => {
      const ownerMatched = item.ownerUserId === userId;
      const homeMatched = homeId ? item.homeId === homeId : true;
      const roomMatched = room ? item.roomName.includes(room) : true;
      const deviceMatched =
        item.name.includes(keyword) ||
        this.matchTypeAlias(item.type, keyword);

      return ownerMatched && homeMatched && roomMatched && deviceMatched;
    });
  }

  private matchTypeAlias(type: SmartDevice['type'], keyword: string): boolean {
    const aliases: Record<SmartDevice['type'], string[]> = {
      LIGHT: ['灯', '照明', '灯光'],
      AIR_CONDITIONER: ['空调'],
      CURTAIN: ['窗帘', '电动帘'],
      LOCK: ['门锁', '智能锁']
    };
    return aliases[type].some((item: string) => keyword.includes(item));
  }
}

6.3 风险策略

设备控制不能只考虑“能不能执行”,还要判断“是否适合直接执行”。

export class RiskPolicy {
  needConfirmation(
    device: SmartDevice,
    action: DeviceAction,
    source: DeviceIntent['source']
  ): boolean {
    if (device.type === 'LOCK') {
      return true;
    }

    if (source === 'VOICE' &&
        device.type === 'AIR_CONDITIONER' &&
        action === DeviceAction.ON) {
      return false;
    }

    return false;
  }
}

真实项目中建议把以下动作列为高风险:

  • 门锁开锁;
  • 燃气阀开启;
  • 烤箱、取暖器等高功率设备启动;
  • 安防撤防;
  • 涉及儿童或老人安全的设备操作。

6.4 设备命令服务

export class DeviceCommandService {
  private executedRequests: Set<string> = new Set<string>();

  async execute(
    requestId: string,
    device: SmartDevice,
    action: DeviceAction,
    value?: number
  ): Promise<CommandResult> {
    if (this.executedRequests.has(requestId)) {
      return {
        status: CommandStatus.SUCCESS,
        message: '该指令已执行,无需重复操作',
        deviceId: device.id,
        deviceName: device.name
      };
    }

    if (!device.online) {
      return {
        status: CommandStatus.OFFLINE,
        message: `${device.roomName}${device.name}当前离线`,
        deviceId: device.id,
        deviceName: device.name,
        retryable: true
      };
    }

    this.validateValue(device, action, value);

    try {
      await this.sendToDevice(device, action, value);
      this.executedRequests.add(requestId);

      return {
        status: CommandStatus.SUCCESS,
        message: this.buildSuccessMessage(device, action, value),
        deviceId: device.id,
        deviceName: device.name
      };
    } catch (error) {
      return {
        status: CommandStatus.FAILED,
        message: '设备控制失败,请稍后重试',
        deviceId: device.id,
        deviceName: device.name,
        retryable: true
      };
    }
  }

  private validateValue(
    device: SmartDevice,
    action: DeviceAction,
    value?: number
  ): void {
    if (action === DeviceAction.SET_LEVEL) {
      if (value === undefined || value < 0 || value > 100) {
        throw new Error('亮度值必须在 0 到 100 之间');
      }
    }

    if (action === DeviceAction.SET_TEMPERATURE) {
      if (device.type !== 'AIR_CONDITIONER') {
        throw new Error('当前设备不支持温度设置');
      }
      if (value === undefined || value < 16 || value > 30) {
        throw new Error('空调温度必须在 16 到 30 摄氏度之间');
      }
    }
  }

  private async sendToDevice(
    device: SmartDevice,
    action: DeviceAction,
    value?: number
  ): Promise<void> {
    // 此处替换为真实家庭中枢、云端 IoT SDK 或局域网协议调用。
    await new Promise<void>((resolve) => {
      setTimeout(() => resolve(), 120);
    });
  }

  private buildSuccessMessage(
    device: SmartDevice,
    action: DeviceAction,
    value?: number
  ): string {
    const prefix = `${device.roomName}${device.name}`;

    switch (action) {
      case DeviceAction.ON:
        return `${prefix}已打开`;
      case DeviceAction.OFF:
        return `${prefix}已关闭`;
      case DeviceAction.OPEN:
        return `${prefix}已打开`;
      case DeviceAction.CLOSE:
        return `${prefix}已关闭`;
      case DeviceAction.SET_LEVEL:
        return `${prefix}亮度已调到${value}%`;
      case DeviceAction.SET_TEMPERATURE:
        return `${prefix}已调到${value}摄氏度`;
      default:
        return `${prefix}控制成功`;
    }
  }
}

6.5 意图分发器

export class IntentDispatcher {
  constructor(
    private repository: DeviceRepository,
    private commandService: DeviceCommandService,
    private riskPolicy: RiskPolicy
  ) {}

  async dispatch(
    userId: string,
    intent: DeviceIntent
  ): Promise<CommandResult> {
    if (intent.intentName !== 'controlDevice') {
      return {
        status: CommandStatus.FAILED,
        message: '暂不支持该意图'
      };
    }

    if (!intent.device || intent.device.trim().length === 0) {
      return {
        status: CommandStatus.NEED_SELECT,
        message: '请告诉我要控制哪个设备'
      };
    }

    const candidates = await this.repository.findCandidates(
      userId,
      intent.homeId,
      intent.room,
      intent.device
    );

    if (candidates.length === 0) {
      return {
        status: CommandStatus.FAILED,
        message: '没有找到匹配的设备'
      };
    }

    if (candidates.length > 1) {
      return {
        status: CommandStatus.NEED_SELECT,
        message: '找到多个设备,请选择一个',
        candidates: candidates.map((item: SmartDevice) => ({
          id: item.id,
          name: item.name,
          roomName: item.roomName,
          online: item.online
        }))
      };
    }

    const device = candidates[0];

    if (this.riskPolicy.needConfirmation(
      device,
      intent.action,
      intent.source
    )) {
      return {
        status: CommandStatus.NEED_CONFIRM,
        message: `是否确认控制${device.roomName}${device.name}`,
        deviceId: device.id,
        deviceName: device.name
      };
    }

    return this.commandService.execute(
      intent.requestId,
      device,
      intent.action,
      intent.value
    );
  }
}

6.6 页面或意图处理入口

@Entry
@Component
struct IntentResultPage {
  @State resultText: string = '正在执行设备指令…';
  @State loading: boolean = true;

  private dispatcher: IntentDispatcher = new IntentDispatcher(
    new DeviceRepository(),
    new DeviceCommandService(),
    new RiskPolicy()
  );

  async aboutToAppear(): Promise<void> {
    const intent: DeviceIntent = {
      intentName: 'controlDevice',
      room: '客厅',
      device: '灯',
      action: DeviceAction.ON,
      requestId: `${Date.now()}-voice`,
      source: 'VOICE'
    };

    const result = await this.dispatcher.dispatch('user-001', intent);
    this.resultText = result.message;
    this.loading = false;
  }

  build() {
    Column({ space: 20 }) {
      Text('智能家庭')
        .fontSize(28)
        .fontWeight(FontWeight.Bold)

      if (this.loading) {
        LoadingProgress()
          .width(48)
          .height(48)
      }

      Text(this.resultText)
        .fontSize(22)
        .fontWeight(FontWeight.Medium)

      Button('返回家庭')
        .onClick(() => {
          // 返回应用主页面
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .padding(24)
  }
}

七、完整执行时序

在这里插入图片描述

“打开客厅灯”的完整执行过程如下:

  1. 用户通过语音或搜索表达目标;
  2. 系统匹配 controlDevice 意图;
  3. 系统抽取 room=客厅device=灯action=ON
  4. 应用校验账号、家庭和设备归属;
  5. 应用解析唯一设备;
  6. 风险策略判断是否需要二次确认;
  7. 设备控制服务下发指令;
  8. 设备返回执行结果;
  9. 应用更新状态缓存并写入日志;
  10. 系统以语音、卡片或页面反馈结果。

八、结果页设计

在这里插入图片描述

结果页不应只是“控制成功”四个字,而应包含:

  • 设备名称和房间;
  • 当前在线状态;
  • 最新设备状态;
  • 执行动作;
  • 撤销入口;
  • 继续控制入口;
  • 指令来源;
  • 失败时的重试方式。

对于语音场景,建议优先给出简短反馈:

“客厅灯已打开。”

对于搜索卡片,可展示更完整的信息:

客厅主灯
在线 · 已打开
[关闭] [继续控制]

九、异常与边界场景

9.1 设备重名

用户说“打开灯”,但家中有多个灯。此时不要随机选择,应返回候选项:

{
  status: CommandStatus.NEED_SELECT,
  message: '找到多个灯,请选择一个',
  candidates: [
    { id: '1', roomName: '客厅', name: '主灯', online: true },
    { id: '2', roomName: '卧室', name: '床头灯', online: true }
  ]
}

9.2 设备离线

应区分“指令发送失败”和“设备确认离线”:

  • 明确离线:提示检查设备供电和网络;
  • 云端超时:提示稍后重试;
  • 局域网不可达:尝试云端链路;
  • 状态未知:提示“指令已发送,设备状态待确认”。

9.3 参数缺失

用户说“把空调调低一点”,缺少目标温度。可以采用两种策略:

  • 结合当前温度做相对调节;
  • 追问“要调到多少度”。

不要把“调低一点”硬编码成固定温度,否则可能不符合用户预期。

9.4 权限失效

家庭成员权限可能被撤销。执行前应重新校验:

  • 用户是否仍属于该家庭;
  • 是否拥有目标设备控制权限;
  • 是否允许从语音入口执行;
  • 是否存在儿童模式或访客限制。

9.5 高风险动作

对于开锁、撤防、启动高功率设备等动作,建议使用:

  • 二次确认;
  • 生物识别;
  • 可信设备校验;
  • 地理围栏;
  • 时间段策略;
  • 审计日志。

十、性能优化

一步直达的用户感知非常敏感。即使最终控制成功,等待时间过长也会让用户误以为系统没有识别。

建议把链路拆成以下指标:

指标 建议目标 说明
意图匹配耗时 < 150 ms 不含大模型远端推理
本地设备解析 < 50 ms 优先使用本地索引
权限校验 < 100 ms 缓存家庭成员关系
指令下发 < 500 ms 取决于设备协议
首次反馈 < 800 ms 可先反馈“正在打开”
最终状态确认 < 2 s 超时后进入待确认状态

10.1 本地索引

把设备名称、房间、别名和最近使用记录建立本地索引,避免每次都拉取全量设备列表。

10.2 状态缓存

设备状态应带时间戳:

interface CachedDeviceState {
  deviceId: string;
  online: boolean;
  powerOn?: boolean;
  updatedAt: number;
}

缓存只用于快速展示,真正执行高风险动作前仍需校验服务端状态。

10.3 分阶段反馈

当设备链路较慢时,可先反馈:

“正在打开客厅灯。”

收到最终结果后再更新为:

“客厅灯已打开。”

这样可以降低用户的不确定感。


十一、测试方案

11.1 功能测试

至少覆盖:

  • 精确设备名;
  • 房间 + 设备名;
  • 设备别名;
  • 动作同义词;
  • 数值参数;
  • 多设备重名;
  • 设备离线;
  • 权限不足;
  • 网络超时;
  • 重复请求;
  • 高风险确认。

11.2 示例测试用例

interface IntentTestCase {
  utterance: string;
  expectedRoom?: string;
  expectedDevice: string;
  expectedAction: DeviceAction;
  expectedValue?: number;
}

const cases: IntentTestCase[] = [
  {
    utterance: '打开客厅灯',
    expectedRoom: '客厅',
    expectedDevice: '灯',
    expectedAction: DeviceAction.ON
  },
  {
    utterance: '把卧室空调调到26度',
    expectedRoom: '卧室',
    expectedDevice: '空调',
    expectedAction: DeviceAction.SET_TEMPERATURE,
    expectedValue: 26
  },
  {
    utterance: '关掉书房窗帘',
    expectedRoom: '书房',
    expectedDevice: '窗帘',
    expectedAction: DeviceAction.CLOSE
  }
];

11.3 体验测试

技术正确不等于体验优秀,还应关注:

  • 用户是否需要重复表达;
  • 设备歧义时是否容易选择;
  • 失败原因是否清晰;
  • 是否能快速撤销;
  • 语音反馈是否过长;
  • 搜索卡片是否展示了关键状态;
  • 高风险确认是否打断过度。

十二、与传统 DeepLink 方案的区别

DeepLink 擅长的是“打开某个页面”,例如:

smarthome://device/detail?id=light-living-main

它解决的是路由问题,但不能天然解决:

  • 用户说了什么;
  • 用户真正想做什么;
  • “打开”是打开页面还是打开设备;
  • 设备名称如何消歧;
  • 参数如何抽取;
  • 高风险动作如何确认;
  • 执行结果如何反馈。

意图框架的价值在于把入口从“页面地址”升级为“业务目标”。两者并非互斥:

  • 意图框架负责理解目标;
  • 应用服务负责执行目标;
  • DeepLink 可作为结果页或详情页跳转手段。

十三、工程落地建议

13.1 不要把业务逻辑写进 UIAbility

UIAbility 应负责生命周期和入口承接,设备解析、权限判断和命令执行应放到独立服务中,便于:

  • 单元测试;
  • 多入口复用;
  • 服务卡片复用;
  • 后续接入智能体;
  • 日志与审计统一。

13.2 先支持少量高频意图

不要一开始就覆盖所有设备和所有句式。可先选择:

  • 开灯 / 关灯;
  • 空调温度调节;
  • 窗帘开合;
  • 常用场景启动。

把高频链路做稳定,再逐步扩展。

13.3 让失败也可恢复

失败反馈必须告诉用户下一步能做什么:

  • “设备离线,请检查电源”;
  • “找到两个客厅灯,请选择”;
  • “当前账号没有控制权限”;
  • “网络超时,是否重试”。

13.4 建立可观测性

建议记录:

interface IntentAuditLog {
  requestId: string;
  userId: string;
  intentName: string;
  source: string;
  targetDeviceId?: string;
  action: string;
  status: string;
  durationMs: number;
  createdAt: number;
}

注意日志中不要记录原始敏感语音、家庭地址或不必要的个人信息。


十四、总结

智能家居应用真正的竞争力,不只是“能连接多少设备”,而是用户完成一次控制需要付出多少操作成本。

基于 HarmonyOS 6.1 Intents Kit,可以把“打开应用—进入家庭—选择房间—找到设备—执行控制”的多步操作,压缩为一句话或一次搜索。要把这个能力做好,关键不在于增加一个入口,而在于建立完整的业务闭环:

  1. 用标准意图描述可执行能力;
  2. 把自然语言映射为结构化参数;
  3. 用统一领域服务解析设备并执行命令;
  4. 对重名、离线、权限和高风险动作做显式处理;
  5. 用语音、卡片和页面返回可理解、可恢复的结果;
  6. 通过幂等、缓存、日志和性能指标保障工程质量。

当应用从“让用户寻找功能”转变为“主动承接用户目标”,智能家居才真正具备一步直达的体验价值。


转载自:https://blog.csdn.net/u014727709/article/details/162996006
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐