标签 鸿蒙开发 下的文章

在这里插入图片描述

摘要

随着鸿蒙应用逐步走向国际化,应用不再只面对中文和英文用户。
中东、北非 等地区,阿拉伯语、希伯来语 这类 从右到左(RTL)语言 是主流,如果应用在这些语言环境下:

  • 布局顺序是反的
  • 返回按钮方向不对
  • 文字对齐看着很别扭

那基本可以直接劝退用户。

好消息是:
鸿蒙系统对 RTL 是原生支持的,而且大部分情况下是“自动完成”的。
坏消息是:
一旦你写了不该写的代码,系统也救不了你。

这篇文章就从真实开发角度,聊清楚鸿蒙里 RTL 适配到底该怎么做、哪些地方最容易踩坑,以及在真实页面里该怎么写。

引言

在早期做 Android / Web 国际化时,RTL 基本属于“高级需求”,很多项目甚至直接忽略。
但在鸿蒙生态里,国际化是默认要考虑的事情,尤其是:

  • 智能设备出海
  • 海外 ROM
  • 多语言系统级应用

在这些场景下,RTL 不再是“锦上添花”,而是基础能力

鸿蒙的设计理念其实很明确:

系统帮你做方向适配,你只要别把方向写死。

问题就在于:
很多开发者在不知不觉中,把方向写死了。

鸿蒙对 RTL 的整体支持机制

系统层是自动感知的

当系统语言切换为 RTL 语言时,鸿蒙会自动做这些事情:

  • 整体布局方向切换为 RTL
  • 文本阅读方向切换
  • Row / Flex 子组件顺序镜像
  • 列表、导航组件交互方向变化

前提只有一个:
你的代码要写得“语义化”。

布局方向适配的核心原则

永远不要写死 left / right

这是 RTL 适配里最常见、也是最致命的问题

错误示例(真实项目里经常看到)

Text('返回')
  .margin({ left: 16 })

这段代码在中文、英文环境下完全正常,
但在 RTL 环境下:

  • 系统已经整体翻转
  • 你又强行加了 left
  • 结果就是布局看起来“很怪”

正确示例(推荐写法)

Text('返回')
  .margin({ start: 16 })

这里的 start 是一个语义方向

  • LTR 语言下等价于 left
  • RTL 语言下等价于 right

你不用管语言,系统会帮你算。

Demo:基础 RTL 自适应 Row

下面是一个可以直接运行的 Demo,你只需要切换系统语言就能看到效果。

@Entry
@Component
struct RtlBaseDemo {
  build() {
    Row() {
      Image($r('app.media.arrow'))
        .width(24)
        .height(24)

      Text('返回')
        .margin({ start: 8 })
    }
    .padding({ start: 16, end: 16 })
  }
}

这个 Demo 的特点:

  • 没有写 left / right
  • 没有强制方向
  • 图标和文字顺序会自动镜像

在阿拉伯语系统下,你会发现:

  • 箭头跑到了右侧
  • 文本在左
  • 间距依然正确

文本方向与对齐的正确方式

文本不要写 Left / Right 对齐

很多人习惯性这样写:

Text('مرحبا')
  .textAlign(TextAlign.Left)

问题是:
Left 在 RTL 里并不是“阅读起点”。

正确的写法是:

Text('مرحبا')
  .textAlign(TextAlign.Start)

系统会自动判断:

  • 英文 → 左对齐
  • 阿拉伯语 → 右对齐

Demo:多语言文本展示

@Entry
@Component
struct TextAlignDemo {
  build() {
    Column() {
      Text('Hello HarmonyOS')
        .textAlign(TextAlign.Start)
        .fontSize(18)

      Text('مرحبا هارموني')
        .textAlign(TextAlign.Start)
        .fontSize(18)
    }
    .padding(16)
  }
}

这个 Demo 非常适合用来自测
切换系统语言,你能直观看到对齐方向变化。

结合真实业务场景的 RTL 适配实践

场景一:应用顶部导航栏

这是 RTL 最容易翻车的地方。

典型需求

  • 返回按钮
  • 页面标题

正确实现方式

@Component
struct TitleBar {
  build() {
    Row() {
      Image($r('app.media.back'))
        .width(24)
        .height(24)

      Text('设置')
        .margin({ start: 12 })
        .fontSize(20)
    }
    .padding(16)
  }
}

这里的关键点:

  • 不指定 FlexDirection
  • 使用 start 间距
  • 图标自动镜像

系统语言一换,整个标题栏方向自然就对了。

场景二:设置页列表项

设置页通常是左右结构,比如:

  • 左边是标题
  • 右边是开关或箭头

推荐写法

@Component
struct SettingItem {
  build() {
    Row() {
      Text('通知')
        .layoutWeight(1)

      Image($r('app.media.arrow'))
        .width(16)
    }
    .padding({ start: 16, end: 16, top: 12, bottom: 12 })
  }
}

在 RTL 下:

  • 文本会靠右
  • 箭头会跑到左侧
  • 整体阅读顺序符合习惯

你不需要为 RTL 单独写一套 UI。

场景三:列表页面与滑动方向

鸿蒙的 List 在 RTL 下:

  • 排列顺序自动调整
  • 滑动方向符合阅读习惯

示例代码

@Entry
@Component
struct ListDemo {
  build() {
    List() {
      ForEach(['Item A', 'Item B', 'Item C'], (item: string) => {
        ListItem() {
          Text(item)
            .padding(16)
            .textAlign(TextAlign.Start)
        }
      })
    }
  }
}

只要你不去强制对齐方向,列表在 RTL 下基本是“零成本适配”。

QA:开发中常见问题

Q1:需要手动判断当前是不是 RTL 吗?

一般不需要。
90% 的页面交给系统就够了。

只有在:

  • 自定义绘制
  • 特殊动画
  • 非标准交互

这些场景下,才需要手动处理。

Q2:图片什么时候需要手动镜像?

  • 返回箭头
  • 方向性极强的图标

可以使用:

Image($r('app.media.arrow'))
  .mirror(true)

普通装饰性图片不建议镜像。

Q3:为什么我写了 start / end 还是不生效?

通常是因为:

  • 强制写了 FlexDirection.Row
  • 写死了 Alignment.Left
  • 在父容器里破坏了方向规则

RTL 出问题,优先回头检查是不是哪一层写死了方向

总结

鸿蒙里的 RTL 适配,其实不是“多写代码”,而是“少犯错误”。

一句话经验总结:

  • start / end
  • TextAlign.Start
  • 不强制方向
  • 相信系统

只要遵守这几条规则,
绝大多数 RTL 问题都会在你“什么都没做”的情况下自动解决。

在这里插入图片描述

摘要

随着智能终端越来越多,应用早就不再只运行在一台设备上。手机、平板、智慧屏、手表之间的协作,已经成了很常见的需求。在这种背景下,多设备任务该怎么分、分到哪台设备执行,就成了开发中绕不开的问题。

在鸿蒙系统中,这个问题并不是靠开发者“手动指定设备”来解决的,而是通过 设备能力感知 + 分布式调度机制 来完成。开发者更多关心的是:
这个任务适合干什么,而不是非要在哪台设备干。

本文会结合鸿蒙系统的分布式能力,介绍多设备任务分配的整体思路,并通过可运行的 Demo 代码,把这个过程完整跑一遍,最后再结合几个真实场景,聊聊它在实际项目中该怎么用。

引言

如果放在以前,一个应用基本只跑在一台手机上,最多考虑前后台切换。但现在不一样了:

  • 手机在你手里
  • 平板在桌子上
  • 智慧屏在客厅
  • 手表戴在手上

用户希望的是:
设备不同,但体验是连着的。

鸿蒙系统的分布式能力,正是为这种场景设计的。它不是简单的“跨设备通信”,而是把 任务、数据、能力 都变成可以在多设备之间流动的资源。

而多设备任务分配,本质上就是一句话:

把合适的任务,交给合适的设备去做。

鸿蒙多设备任务分配的整体思路

先发现设备,再谈分配

在鸿蒙系统中,只要设备在同一个分布式网络里,系统就能自动发现它们。
开发者不需要自己维护“设备表”,也不用关心设备什么时候上线、下线。

系统会帮你感知这些信息:

  • 设备类型(手机、平板、智慧屏)
  • 基本性能情况
  • 是否可信
  • 当前是否可用

你只需要在合适的时机拿到设备列表即可。

任务一定要能拆

多设备任务分配的前提是:
你的业务本身是能拆开的。

比如:

  • 页面展示是一块
  • 数据采集是一块
  • 计算处理是一块

如果一个任务从头到尾全写死在一个 Ability 里,那基本就没法分配了。

系统负责“怎么选设备”

在鸿蒙里,真正“选哪台设备执行”的逻辑,大部分是系统完成的:

  • 当前设备忙不忙
  • 网络情况好不好
  • 设备能力是否匹配
  • 是否更适合本地执行

开发者更多是通过 Ability 启动方式、Service 类型、数据同步方式 来间接影响分配结果。

核心实现方式一:跨设备启动 Ability

适合什么场景

这种方式最常见,适合:

  • 页面展示
  • 功能模块整体迁移
  • 用户可感知的交互任务

比如:
手机负责控制,平板负责显示大屏内容。

Demo:在平板上启动远程 Ability

import distributedDeviceManager from '@ohos.distributedDeviceManager';
import featureAbility from '@ohos.ability.featureAbility';

const BUNDLE_NAME = 'com.example.distributeddemo';

let deviceManager = distributedDeviceManager.createDeviceManager(BUNDLE_NAME);

function startRemotePage() {
  let devices = deviceManager.getTrustedDeviceListSync();

  devices.forEach(device => {
    if (device.deviceType === 2) { // 假设 2 表示平板
      let want = {
        bundleName: BUNDLE_NAME,
        abilityName: 'RemotePageAbility',
        deviceId: device.deviceId
      };
      featureAbility.startAbility(want);
    }
  });
}

代码说明

  • createDeviceManager:创建设备管理器
  • getTrustedDeviceListSync:获取可信设备列表
  • deviceType:用于简单区分设备类型
  • startAbility:指定 deviceId 后,Ability 会在远端设备启动

整个过程不需要你关心远端设备的进程、生命周期,系统会处理。

核心实现方式二:分布式 Service 执行任务

适合什么场景

这种方式更适合:

  • 计算密集型任务
  • 后台处理
  • 不需要 UI 的逻辑

比如:
手机采集数据,交给性能更强的设备做分析。

Demo:连接远端计算 Service

import featureAbility from '@ohos.ability.featureAbility';

function connectRemoteService(remoteDeviceId: string) {
  let want = {
    bundleName: 'com.example.distributeddemo',
    abilityName: 'ComputeServiceAbility',
    deviceId: remoteDeviceId
  };

  featureAbility.connectAbility(want, {
    onConnect(elementName, remote) {
      console.log('远程 Service 已连接');
      remote.sendMessage({
        command: 'startCompute',
        data: [1, 2, 3, 4]
      });
    },
    onDisconnect() {
      console.log('远程 Service 已断开');
    }
  });
}

代码说明

  • Service 在远端设备运行
  • 本地通过 IPC 的方式和远端通信
  • 计算逻辑完全在远端执行
  • 本地只负责发请求、收结果

这种方式非常适合“重计算、轻交互”的任务。

典型应用场景分析与示例

场景一:手机 + 平板的学习展示系统

场景说明

  • 手机负责控制、翻页
  • 平板负责展示课件内容

实现思路

  • 手机发现平板
  • 在平板启动展示 Ability
  • 通过分布式数据同步当前页码
import distributedData from '@ohos.data.distributedData';

async function syncPage(page: number) {
  let kvManager = distributedData.createKVManager();
  let store = await kvManager.getKVStore('pageStore');
  await store.put('current_page', page);
}

平板端监听数据变化,自动刷新页面。

场景二:多设备健康数据分析

场景说明

  • 手表采集心率
  • 手机做基础处理
  • 平板做数据可视化

实现思路

  • 手表同步原始数据
  • 手机过滤、预处理
  • 平板负责展示图表

核心在于:
任务不是“复制”,而是“分工”。

场景三:家庭智慧屏协同控制

场景说明

  • 手机是遥控器
  • 智慧屏负责 UI 展示
  • 计算逻辑放在智慧屏

实现思路

  • 手机只负责发指令
  • 智慧屏 Service 处理业务逻辑
  • 结果同步回手机

这种模式下,手机压力很小,体验反而更流畅。

常见问题 QA

Q1:我能不能指定“一定要某台设备执行”?

不推荐。
鸿蒙的设计思想是 声明需求,而不是指定设备
你可以通过能力需求去“引导”,但不建议写死。

Q2:设备突然下线怎么办?

系统会通知连接断开,
你需要做的只有一件事:
支持本地降级执行或重试。

Q3:分布式任务一定比本地慢吗?

不一定。
当任务本身就不适合本地执行时,
分布式反而更快、更省电。

总结

在鸿蒙系统中,多设备任务分配并不是一套复杂、难以理解的机制,它的核心思想其实很简单:

  • 把任务拆清楚
  • 描述好任务需求
  • 把调度交给系统

只要你在设计阶段考虑好“哪些任务适合分出去”,鸿蒙的分布式能力就能自然地帮你把事情做好。

一句话总结就是:

多设备任务分配,不是设备协作有多复杂,而是你有没有把任务设计清楚。

在这里插入图片描述

摘要

随着智能终端越来越多,应用早就不再只运行在一台设备上。手机、平板、智慧屏、手表之间的协作,已经成了很常见的需求。在这种背景下,多设备任务该怎么分、分到哪台设备执行,就成了开发中绕不开的问题。

在鸿蒙系统中,这个问题并不是靠开发者“手动指定设备”来解决的,而是通过 设备能力感知 + 分布式调度机制 来完成。开发者更多关心的是:
这个任务适合干什么,而不是非要在哪台设备干。

本文会结合鸿蒙系统的分布式能力,介绍多设备任务分配的整体思路,并通过可运行的 Demo 代码,把这个过程完整跑一遍,最后再结合几个真实场景,聊聊它在实际项目中该怎么用。

引言

如果放在以前,一个应用基本只跑在一台手机上,最多考虑前后台切换。但现在不一样了:

  • 手机在你手里
  • 平板在桌子上
  • 智慧屏在客厅
  • 手表戴在手上

用户希望的是:
设备不同,但体验是连着的。

鸿蒙系统的分布式能力,正是为这种场景设计的。它不是简单的“跨设备通信”,而是把 任务、数据、能力 都变成可以在多设备之间流动的资源。

而多设备任务分配,本质上就是一句话:

把合适的任务,交给合适的设备去做。

鸿蒙多设备任务分配的整体思路

先发现设备,再谈分配

在鸿蒙系统中,只要设备在同一个分布式网络里,系统就能自动发现它们。
开发者不需要自己维护“设备表”,也不用关心设备什么时候上线、下线。

系统会帮你感知这些信息:

  • 设备类型(手机、平板、智慧屏)
  • 基本性能情况
  • 是否可信
  • 当前是否可用

你只需要在合适的时机拿到设备列表即可。

任务一定要能拆

多设备任务分配的前提是:
你的业务本身是能拆开的。

比如:

  • 页面展示是一块
  • 数据采集是一块
  • 计算处理是一块

如果一个任务从头到尾全写死在一个 Ability 里,那基本就没法分配了。

系统负责“怎么选设备”

在鸿蒙里,真正“选哪台设备执行”的逻辑,大部分是系统完成的:

  • 当前设备忙不忙
  • 网络情况好不好
  • 设备能力是否匹配
  • 是否更适合本地执行

开发者更多是通过 Ability 启动方式、Service 类型、数据同步方式 来间接影响分配结果。

核心实现方式一:跨设备启动 Ability

适合什么场景

这种方式最常见,适合:

  • 页面展示
  • 功能模块整体迁移
  • 用户可感知的交互任务

比如:
手机负责控制,平板负责显示大屏内容。

Demo:在平板上启动远程 Ability

import distributedDeviceManager from '@ohos.distributedDeviceManager';
import featureAbility from '@ohos.ability.featureAbility';

const BUNDLE_NAME = 'com.example.distributeddemo';

let deviceManager = distributedDeviceManager.createDeviceManager(BUNDLE_NAME);

function startRemotePage() {
  let devices = deviceManager.getTrustedDeviceListSync();

  devices.forEach(device => {
    if (device.deviceType === 2) { // 假设 2 表示平板
      let want = {
        bundleName: BUNDLE_NAME,
        abilityName: 'RemotePageAbility',
        deviceId: device.deviceId
      };
      featureAbility.startAbility(want);
    }
  });
}

代码说明

  • createDeviceManager:创建设备管理器
  • getTrustedDeviceListSync:获取可信设备列表
  • deviceType:用于简单区分设备类型
  • startAbility:指定 deviceId 后,Ability 会在远端设备启动

整个过程不需要你关心远端设备的进程、生命周期,系统会处理。

核心实现方式二:分布式 Service 执行任务

适合什么场景

这种方式更适合:

  • 计算密集型任务
  • 后台处理
  • 不需要 UI 的逻辑

比如:
手机采集数据,交给性能更强的设备做分析。

Demo:连接远端计算 Service

import featureAbility from '@ohos.ability.featureAbility';

function connectRemoteService(remoteDeviceId: string) {
  let want = {
    bundleName: 'com.example.distributeddemo',
    abilityName: 'ComputeServiceAbility',
    deviceId: remoteDeviceId
  };

  featureAbility.connectAbility(want, {
    onConnect(elementName, remote) {
      console.log('远程 Service 已连接');
      remote.sendMessage({
        command: 'startCompute',
        data: [1, 2, 3, 4]
      });
    },
    onDisconnect() {
      console.log('远程 Service 已断开');
    }
  });
}

代码说明

  • Service 在远端设备运行
  • 本地通过 IPC 的方式和远端通信
  • 计算逻辑完全在远端执行
  • 本地只负责发请求、收结果

这种方式非常适合“重计算、轻交互”的任务。

典型应用场景分析与示例

场景一:手机 + 平板的学习展示系统

场景说明

  • 手机负责控制、翻页
  • 平板负责展示课件内容

实现思路

  • 手机发现平板
  • 在平板启动展示 Ability
  • 通过分布式数据同步当前页码
import distributedData from '@ohos.data.distributedData';

async function syncPage(page: number) {
  let kvManager = distributedData.createKVManager();
  let store = await kvManager.getKVStore('pageStore');
  await store.put('current_page', page);
}

平板端监听数据变化,自动刷新页面。

场景二:多设备健康数据分析

场景说明

  • 手表采集心率
  • 手机做基础处理
  • 平板做数据可视化

实现思路

  • 手表同步原始数据
  • 手机过滤、预处理
  • 平板负责展示图表

核心在于:
任务不是“复制”,而是“分工”。

场景三:家庭智慧屏协同控制

场景说明

  • 手机是遥控器
  • 智慧屏负责 UI 展示
  • 计算逻辑放在智慧屏

实现思路

  • 手机只负责发指令
  • 智慧屏 Service 处理业务逻辑
  • 结果同步回手机

这种模式下,手机压力很小,体验反而更流畅。

常见问题 QA

Q1:我能不能指定“一定要某台设备执行”?

不推荐。
鸿蒙的设计思想是 声明需求,而不是指定设备
你可以通过能力需求去“引导”,但不建议写死。

Q2:设备突然下线怎么办?

系统会通知连接断开,
你需要做的只有一件事:
支持本地降级执行或重试。

Q3:分布式任务一定比本地慢吗?

不一定。
当任务本身就不适合本地执行时,
分布式反而更快、更省电。

总结

在鸿蒙系统中,多设备任务分配并不是一套复杂、难以理解的机制,它的核心思想其实很简单:

  • 把任务拆清楚
  • 描述好任务需求
  • 把调度交给系统

只要你在设计阶段考虑好“哪些任务适合分出去”,鸿蒙的分布式能力就能自然地帮你把事情做好。

一句话总结就是:

多设备任务分配,不是设备协作有多复杂,而是你有没有把任务设计清楚。

在这里插入图片描述

摘要

随着 HarmonyOS / OpenHarmony 在手机、平板、智慧屏、车机等多设备上的落地,应用的复杂度正在明显提升。页面不再只是简单展示,而是伴随着网络请求、数据计算、设备协同等大量逻辑。如果这些逻辑处理不当,很容易出现页面卡顿、点击无响应,甚至 Ability 被系统回收的问题。

线程阻塞,已经成为鸿蒙应用开发中最容易踩坑、也最影响体验的问题之一。本文将结合实际开发场景,用尽量口语化的方式,聊一聊在鸿蒙系统中如何系统性地避免线程阻塞,并给出可以直接运行的 Demo 代码。

引言

在早期的应用开发中,很多开发者习惯把逻辑直接写在点击事件里,或者在页面加载时同步读取数据。这种写法在简单页面中问题不大,但在 HarmonyOS 这种强调流畅体验和多设备协同的系统中,很容易暴露问题。

鸿蒙的 UI 是声明式的,系统对主线程(UI 线程)非常敏感。一旦主线程被占用,页面掉帧、动画卡住、操作延迟都会立刻出现。因此,理解哪些操作会阻塞线程,以及如何把这些操作合理地“挪走”,是每个鸿蒙开发者绕不开的一课。

下面我们从原理、工具、代码和真实场景几个角度,完整地拆解这个问题。

为什么线程阻塞在鸿蒙中这么致命

UI 线程到底在忙什么

在 HarmonyOS 中,UI 线程主要负责三件事:

  • ArkUI 页面渲染
  • 用户事件分发(点击、滑动等)
  • Ability 生命周期回调

简单理解就是:只要和“看得见、点得动”有关的事情,几乎都在 UI 线程上完成

一旦你在这里做了耗时操作,比如计算、IO、网络等待,页面就会立刻表现出“卡”的感觉。

常见的阻塞来源

在实际项目中,最容易导致阻塞的操作通常包括:

  • 同步网络请求
  • 文件读写
  • 数据库查询
  • 大量 for 循环计算
  • 人为 sleep 或死循环

这些操作本身不一定是错的,问题在于它们被放在了不该放的线程上

鸿蒙中避免线程阻塞的核心思路

一个总原则

可以把鸿蒙里的线程使用总结成一句话:

UI 线程只处理 UI,其他事情交给异步、线程池或 Worker。

围绕这个原则,系统也提供了多种工具,帮助开发者把任务“分流”。

异步编程是第一道防线

使用 async / await 处理耗时逻辑

在 ArkTS 中,官方推荐优先使用 Promise 和 async / await。它的好处是代码结构清晰,而且不会阻塞 UI 线程。

示例:页面加载网络数据

@Entry
@Component
struct AsyncDemo {
  @State message: string = '加载中...'

  build() {
    Column() {
      Text(this.message)
        .fontSize(20)
        .margin(20)

      Button('重新加载')
        .onClick(() => {
          this.loadData()
        })
    }
  }

  async loadData() {
    this.message = '请求中...'
    let response = await fetch('https://example.com/data')
    let result = await response.text()
    this.message = result
  }
}

代码说明

  • loadData 使用 async 声明,不会阻塞 UI
  • await 只是暂停当前函数执行,不会卡住页面
  • UI 更新完全由状态变化驱动

这是最基础、也是最常用的一种防阻塞方式。

TaskPool:处理计算和 IO 的利器

什么时候该用 TaskPool

当你遇到下面这些情况时,TaskPool 几乎是必选项:

  • 大量计算
  • 批量数据处理
  • 文件压缩、解析

可运行 Demo 示例

import taskpool from '@ohos.taskpool'

@Concurrent
function calculateSum(count: number): number {
  let sum = 0
  for (let i = 0; i < count; i++) {
    sum += i
  }
  return sum
}

@Entry
@Component
struct TaskPoolDemo {
  @State result: string = '等待计算'

  build() {
    Column() {
      Text(this.result)
        .fontSize(18)
        .margin(20)

      Button('开始计算')
        .onClick(() => {
          this.startTask()
        })
    }
  }

  startTask() {
    this.result = '计算中...'
    taskpool.execute(calculateSum, 1000000).then(res => {
      this.result = `结果是:${res}`
    })
  }
}

代码说明

  • @Concurrent 表示该函数可以并发执行
  • TaskPool 自动管理线程,不需要开发者手动创建线程
  • UI 线程只负责接收结果和更新状态

在真实项目中,使用 TaskPool 往往能立刻解决页面卡顿问题。

Worker:长期后台任务的选择

Worker 的使用场景

如果任务具有下面这些特点,就更适合使用 Worker:

  • 长时间运行
  • 需要持续处理数据
  • 与 UI 强隔离

比如日志分析、音视频处理、复杂解析等。

示例:使用 Worker 处理数据

主线程代码

let worker = new Worker('workers/data_worker.ts')

worker.postMessage({ action: 'start' })

worker.onmessage = (e) => {
  console.log('收到结果:', e.data)
}

Worker 线程代码

onmessage = function (e) {
  if (e.data.action === 'start') {
    let result = 0
    for (let i = 0; i < 500000; i++) {
      result += i
    }
    postMessage(result)
  }
}

代码说明

  • Worker 与 UI 线程完全独立
  • 即使计算时间较长,也不会影响页面交互
  • 通过消息机制进行通信

结合实际场景的应用示例

场景一:列表页面加载大量数据

问题:

  • 首次进入页面时一次性处理全部数据
  • 页面明显卡顿

解决思路:

  • 网络请求使用 async
  • 数据整理放入 TaskPool
async loadList() {
  let data = await fetchData()
  taskpool.execute(processData, data).then(list => {
    this.list = list
  })
}

场景二:文件导入与解析

问题:

  • 文件较大
  • 解析过程耗时

解决思路:

  • Worker 负责解析
  • UI 只显示进度
worker.postMessage({ filePath })

场景三:复杂计算驱动 UI 更新

问题:

  • 计算逻辑和 UI 耦合

解决思路:

  • 计算完全放到 TaskPool
  • UI 只订阅结果

QA 环节

Q:async / await 会不会阻塞线程?
A:不会,它只是让出执行权,不会卡住 UI 线程。

Q:TaskPool 和 Worker 怎么选?
A:短期、一次性的任务优先 TaskPool,长期或持续任务用 Worker。

Q:能不能在生命周期里做耗时操作?
A:不建议,生命周期函数应尽量轻量。

总结

线程阻塞并不是某一个 API 的问题,而是设计问题。在 HarmonyOS 中,系统已经为我们准备好了异步模型、TaskPool 和 Worker,只要遵循“UI 线程只做 UI”的原则,大多数卡顿问题都可以提前避免。

在真实项目中,提前做好任务拆分、线程规划,比后期排查卡顿要省心得多。这也是鸿蒙开发从“能跑”到“跑得顺”的一个重要分水岭。

用代码浇灌春天,最终必将见证万紫千红的生态盛景。

她说:“我愿在这群芳争艳的时代,绽放一抹‘吉祥’红!”

吉林银行作为吉林省经济发展的 “金融引擎”,在数字化转型浪潮中勇立潮头。其开发团队通过分布式架构重构、ArkUI-X 框架迁移及原子化服务开发等技术突破,历时 21 个自然日完成 HarmonyOS NEXT 核心功能版本适配。今天让我们采访一下吉林银行的鸿蒙开发者代表卢妍娆女士,一起听她讲讲应用适配 HarmonyOS NEXT 的故事。

自 22 年加入吉林银行以来,卢妍娆便先后投入到了新一代核心系统建设以及吉林银行手机银行 6.0 迭代建设。23 年年末吉林银行对应用鸿蒙化表示明确认可,认为鸿蒙生态适配不仅仅是吉林银行构建数字金融护城河的战略突破口,更是实现技术自主可控的关键战役,如春潮涌动时抢占滩头的先锋。

“我们非常期待能在 HarmonyOS NEXT 这个种满花卉的生态里,迅速绽放并共同成长,掌握一定的话语权。”

在“打仗”之前,吉林银行研发团队完成了鸿蒙开发的学习,并于 2024 年 2 月与华为达成鸿蒙适配的合作意向。“华为为我们提供了技术上的答疑指导,帮助我们打通开发道路,让后面的开发更加便利。”万事俱备只欠东风,2024 年 5 月底立项申请通过,项目正式启动,基于手机银行 6.0 功能及性能提升后的框架,6 月 18 日正式上架核心交易功能版本。

卢妍娆在 HDD 活动照片

“HarmonyOS NEXT 跟安卓不一样,是个全新的系统,也是全新的体验”

卢妍娆最初担心,吉林银行 App 适配鸿蒙的时候会很困难,因为原有的代码架构需要大规模重构。在鸿蒙声明式开发里,UI 是通过声明式语法描述的,需要重新编写大量的 UI 代码。事实上,开发过程真的很艰难吗?

“遇到技术难题的时候,你可以直接提出问题,鸿蒙的官方技术人员会回复,甚至提供样例代码手把手帮你解决问题。例如,我们开发团队在遇到微信分享无法获取 uicontext,自定义弹窗无法展示的问题时,华为团队提供了示例代码解决问题;由于医保缴费框架存在中断逻辑,导致页面存在多次跳转,华为团队根据每次 ID 的不同,提供样例代码规避了消费者界面多次跳转的问题;开发语音识别功能的时候,我们团队没有足够的经验,华为技术人员提供了语音识别代码 Demo 以及 UI 代码,帮助我们快速实现语音识别功能。”卢妍娆回忆道。相比安卓开发中依赖第三方论坛的“投石问路”,鸿蒙的这种开发者帮扶模式更高效更贴心。

应用适配鸿蒙生态架构

HarmonyOS SDK 接入:纯净之境,开启开发新篇章

“我们的手机银行集成第三方 SDK 有 18 个,HarmonyOS SDK 替代了部分,不仅协同加速,提升了我们开发的效率,还为我们节省了大量成本。” 卢妍娆跟我介绍她们的应用。

传统 SDK 在架构设计上往往存在冗余和复杂的问题,在接入时会引入大量不必要的代码和依赖库。而 HarmonyOS SDK 采用的原子化服务架构,将功能拆解为最小可复用单元,使用起来就像搭建积木一样,我们可以根据需求灵活选择和组合这些原子化服务。这种模块化设计使得代码更加简洁、清晰,如同月光下的水晶棱镜,每一个模块都剔透纯净。以一个简单的天气卡片组件为例,在 HarmonyOS SDK 中,开发者可以通过简洁的代码实现其功能,非常高效简洁。

小组开会研讨方案

"HarmonyOS NEXT 不是简单的系统升级,而是给开发者重新定义了工具类应用的魔法棒。当设备间的界限消失,我们才能真正聚焦于用户需求本身。"对于吉林银行来说,鸿蒙生态带来的意义不仅仅优先他人一步,更重要的是带来了万物互联的时代。

夜幕降临,金融街的灯火次第亮起。在这场由鸿蒙系统掀起的数字化浪潮中,银行正从传统的 "金融服务提供者" 转变为 "智能生态构建者"。当吉林银行以金融级安全纽带编织起千万用户的数字生活场景,既筑牢数字经济时代的安全护城河,又为银行生态的生长埋下战略伏笔;当意图框架读懂用户每一个潜在需求,各个企业正在书写属于自己的全场景智慧篇章。而这,仅仅是鸿蒙星河下的序章。

了解鸿蒙开发认证详情,探索鸿蒙开发者联盟丰富资源,点击链接:​​鸿蒙开发者联盟​​。​这里有开发文档、论坛、工具等,快加入,开启鸿蒙开发之旅!

最近奇思妙想群里最热的讨论,莫过于华为鸿蒙的亿元激励活动。

活动介绍:开发者成功报名本计划,且在 2025 年 7 月 23 日至 2025 年 12 月 31 日完成鸿蒙应用、游戏、元服务开发,并正式上架至华为应用市场,将有机会获得现金激励( 1 万元一款)。

群里成果:有刚开始不信后面看大家出结算单然后通宵开发上架十几款,也有在宿舍连夜奋战上架 20 款的学生,还有拿到激励要辞职 all in 的,更有找准赛道连上 100 款的,大部分开发者上架十几款。
群里都是在相互鼓励、相互刺激,大家松懈时,就会有群友发出一个审核上架通知,全体都提起精神开发。

对于鸿蒙活动,很多人会有两种截然不同的反应:

一种是:“xx 你都信,这么天真。”

另一种是:“先相信,干就完了。”

具体可看站内的历史鸿蒙帖子的评论区...

这两种反应的背后,折射出的正是人与人之间赚钱能力的本质差异——认知差。

借着鸿蒙激励活动,来聊聊“认知与赚钱”。

一、 所有的暴利,都源于“供需失衡”的窗口期

为什么华为要发钱?因为它急需在短时间内,把应用商店的货架填满。

这就造成了一个短期的、剧烈的供需失衡

  • 需求端: 华为希望拉升纯血鸿蒙应用的数量。
  • 供给端: 参与鸿蒙开发的开发者很少,市面上的应用极度匮乏。

现在的鸿蒙生态,就是一片“荒地”。在成熟的安卓或 iOS 生态里,你做一个记账软件,可能连水花都砸不出来,因为竞争者有几万个。但在现在的纯血鸿蒙里,你做一个记账软件,可能就是“官方推荐”,因为根本没几个好用的竞品。

赚钱的第一层认知是:识别红利。
红利就是平台为了活下去,不得不让渡出的超额利润。当年的淘宝店、后来的微信公众号、前几年的抖音直播,以及现在的鸿蒙开发,逻辑如出一辙。

那些赚到大钱的人,往往不是因为技术最牛,而是因为他们在平台最饥渴的时候,递了一瓶水。

顺势而为,还是看笑话?

二、 认知的诅咒:为什么聪明人反而赚不到“第一桶金”?

在鸿蒙激励活动出来后,我观察到一个有趣的现象:
很多资深的程序员、技术大牛在冷嘲热讽,他们分析鸿蒙的底层架构,分析生态的各种困难,得出的结论是“这么天真,可操作的地方太多了,赚不到的”。

反而是很多技术一般的“草根开发者”,甚至是一些只会套壳、只会做简单工具的业余程序员,第一时间冲了进去。他们不管架构完不完美,先把 App 上架,接着就是相信鸿蒙打钱。

这就引出了赚钱的第二层认知:执行力往往大于完美主义。

许多“聪明人陷入了“认知的诅咒”。他们想得太多,看得太远,总想等到局势明朗、风险为零的时候再出手。
但商业规律是残酷的:当一件事变得没有任何风险时,它也就没有任何超额收益了。

鸿蒙现在确生态确实不成熟,文档也不完善。但正是因为这些“门槛”和“麻烦”,才挡住了大厂的标准化倾轧,给个体和小团队留出了套利的空间。

真正的高认知,不是预判困难并止步,而是预判了困难,并计算出即使算上解决困难的成本,收益依然可观,然后果断入局。

结语

这世界上最遥远的距离,不是生与死,而是你就站在风口上,却觉得那是阵乱吹的风。

对于程序员来说,鸿蒙不仅是一个技术生态,更是一面镜子。它照出了我们面对新生事物时的态度:
是习惯性地质疑、观望?
还是敏锐地拆解、试错?

你所赚的每一分钱,都是你对这个世界认知的变现;你所亏的每一分钱,都是因为对这个世界认知有缺陷。

所以,先相信

-

有其他想法讨论欢迎进奇思妙想群(一个可能会让你赚钱的群),v 站上最大最久的程序员群: