鸿蒙开发系列 · 专家篇
前言
经过17篇的积累,我们已经构建了一个功能丰富的语音备忘录应用:ArkUI声明式UI、网络请求、数据持久化、路由导航、自定义组件、元服务、安全权限、推送后台、音视频、地图定位、账号支付、端侧AI。它在HarmonyOS上跑得很漂亮。
但现实往往更复杂。你的产品可能还需要覆盖Android用户,或者公司要求一套代码同时服务多个平台。重写一套Android(Kotlin/Jetpack Compose)和iOS(Swift/SwiftUI)实现?意味着三倍开发成本、三倍维护负担、三倍Bug风险。
ArkUI-X是华为给出的答案。它基于ArkTS和ArkUI,通过跨平台运行时让同一套代码运行在HarmonyOS、Android、iOS上。不是"类似语法重新实现",而是真正的同一套源码,条件编译适配差异。
本文将带你理解ArkUI-X的技术原理,搭建跨平台工程,处理平台差异,并最终发布到多个生态。
一、ArkUI-X 技术架构
1.1 架构分层
┌─────────────────────────────────────────────────────────────┐
│ 应用层(你的ArkTS代码) │
│ ArkUI组件 / 业务逻辑 / 状态管理 │
├─────────────────────────────────────────────────────────────┤
│ ArkUI-X 框架层 │
│ 跨平台组件映射 / 平台桥接 / 条件编译 │
├──────────────────────┬───────────────────┬──────────────────┤
│ HarmonyOS 运行时 │ Android 运行时 │ iOS 运行时 │
│ ArkCompiler引擎 │ ArkTS on JVM │ ArkTS on JSC │
│ 原生Ability框架 │ Activity映射 │ UIViewController│
├──────────────────────┴───────────────────┴──────────────────┤
│ 系统原生能力 │
│ 鸿蒙系统服务 / Android SDK / iOS SDK │
└─────────────────────────────────────────────────────────────┘
ArkUI-X的核心设计:
统一的ArkTS语法:开发者始终写ArkTS,无需学习Kotlin或Swift
平台桥接层:框架负责将ArkUI组件映射到各平台的原生UI
条件编译:通过编译宏处理平台差异代码
原生扩展:各平台特有能力通过插件机制接入
1.2 ArkUI-X vs 其他跨平台方案
ArkUI-X的最大差异化:对HarmonyOS的一等公民支持。如果你的产品以鸿蒙为主、Android/iOS为辅,ArkUI-X是架构上最自然的选择。
二、跨平台工程搭建
2.1 环境准备
ArkUI-X开发需要:
DevEco Studio 4.0+(鸿蒙开发)
Android Studio(Android构建)
Xcode 14+(iOS构建)
ArkUI-X SDK
2.2 创建跨平台工程
# 使用ArkUI-X CLI创建工程
npx @arkui-x/cli create my-cross-platform-app
cd my-cross-platform-app
工程结构:
my-cross-platform-app/
├── .arkui-x/ # 跨平台构建配置
├── entry/
│ ├── src/main/ets/ # ArkTS源码(核心)
│ │ ├── pages/ # 页面
│ │ ├── components/ # 组件
│ │ └── utils/ # 工具
│ └── src/main/resources/# 资源文件
├── android/ # Android平台工程
│ ├── app/ # Android应用模块
│ └── build.gradle # Android构建配置
├── ios/ # iOS平台工程
│ ├── MyApp/ # iOS应用目录
│ └── Podfile # iOS依赖配置
└── package.json # 项目配置
2.3 条件编译
条件编译是ArkUI-X处理平台差异的核心机制:
// entry/src/main/ets/utils/PlatformAdapter.ets
// 平台检测常量
const isHarmonyOS: boolean = () => {
return typeof abilityAccessCtrl !== 'undefined';
};
const isAndroid: boolean = () => {
return $r('sys.capability.platform.android') !== undefined;
};
const isIOS: boolean = () => {
return $r('sys.capability.platform.ios') !== undefined;
};
// 平台适配:获取设备信息
export function getDeviceInfo(): { brand: string; model: string; systemVersion: string } {
// 鸿蒙平台
#if defined(HARMONYOS)
return {
brand: deviceInfo.brand,
model: deviceInfo.productModel,
systemVersion: deviceInfo.osFullName
};
#endif
// Android平台
#if defined(ANDROID)
return {
brand: android.os.Build.BRAND,
model: android.os.Build.MODEL,
systemVersion: android.os.Build.VERSION.RELEASE
};
#endif
// iOS平台
#if defined(IOS)
return {
brand: 'Apple',
model: UIDevice.currentDevice.model,
systemVersion: UIDevice.currentDevice.systemVersion
};
#endif
return { brand: 'Unknown', model: 'Unknown', systemVersion: 'Unknown' };
}
2.4 平台桥接:调用原生能力
当ArkTS无法满足需求时,可以通过平台桥接调用原生代码:
// entry/src/main/ets/utils/NativeBridge.ets
// 声明平台方法接口
interface PlatformBridge {
// 获取电池电量
getBatteryLevel(): Promise<number>;
// 震动反馈
vibrate(duration: number): void;
// 分享文本
shareText(text: string): Promise<boolean>;
}
// 桥接实现
class HarmonyOSBridge implements PlatformBridge {
async getBatteryLevel(): Promise<number> {
const batteryInfo = battery.getBatteryInfo();
return batteryInfo.batterySOC; // 电量百分比
}
vibrate(duration: number): void {
vibrator.startVibration({
type: 'time',
duration
});
}
async shareText(text: string): Promise<boolean> {
const want = {
action: 'ohos.want.action.SEND',
parameters: { text }
};
await getContext().startAbility(want);
return true;
}
}
class AndroidBridge implements PlatformBridge {
async getBatteryLevel(): Promise<number> {
// 通过JNI调用Android BatteryManager
return await arkuiX.loadNativeModule('battery').getLevel();
}
vibrate(duration: number): void {
arkuiX.loadNativeModule('vibrator').vibrate(duration);
}
async shareText(text: string): Promise<boolean> {
return await arkuiX.loadNativeModule('share').shareText(text);
}
}
// 工厂函数
export function createPlatformBridge(): PlatformBridge {
#if defined(HARMONYOS)
return new HarmonyOSBridge();
#elif defined(ANDROID)
return new AndroidBridge();
#elif defined(IOS)
return new IOSBridge();
#else
throw new Error('不支持的平台');
#endif
}
三、跨平台UI适配
3.1 屏幕适配
不同平台的屏幕尺寸和Safe Area差异显著:
// entry/src/main/ets/utils/ScreenAdapter.ets
export class ScreenAdapter {
// 获取安全区域(避开刘海、灵动岛、导航栏)
static getSafeArea(): { top: number; bottom: number; left: number; right: number } {
#if defined(HARMONYOS)
const windowClass = window.getLastWindowSync(getContext());
const avoidArea = windowClass.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
return {
top: avoidArea.topRect.height,
bottom: avoidArea.bottomRect.height,
left: avoidArea.leftRect.width,
right: avoidArea.rightRect.width
};
#elif defined(ANDROID)
const insets = arkuiX.loadNativeModule('window').getSafeInsets();
return insets;
#elif defined(IOS)
const safeArea = arkuiX.loadNativeModule('window').getSafeArea();
return safeArea;
#else
return { top: 0, bottom: 0, left: 0, right: 0 };
#endif
}
// 平台特定的底部导航高度
static getBottomNavHeight(): number {
#if defined(IOS)
// iOS有Home Indicator
return 34;
#elif defined(ANDROID)
// Android导航栏高度可变
return 48;
#else
return 0;
#endif
}
}
3.2 跨平台页面框架
// entry/src/main/ets/pages/CrossPlatformPage.ets
import { ScreenAdapter } from '../utils/ScreenAdapter';
@Entry
@Component
struct CrossPlatformPage {
private safeArea = ScreenAdapter.getSafeArea();
build() {
Column() {
// 顶部安全区域占位
Column()
.width('100%')
.height(this.safeArea.top)
.backgroundColor('#2196F3')
// 导航栏
Row() {
Text('跨平台应用')
.fontSize(18)
.fontColor('#FFFFFF')
.fontWeight(FontWeight.Medium)
}
.width('100%')
.height(56)
.padding({ left: 16, right: 16 })
.backgroundColor('#2196F3')
.justifyContent(FlexAlign.Center)
// 内容区
Scroll() {
Column({ space: 16 }) {
this.PlatformInfoCard()
this.FeatureCard()
this.PlatformSpecificCard()
}
.width('100%')
.padding(16)
}
.layoutWeight(1)
// 底部安全区域占位
Column()
.width('100%')
.height(this.safeArea.bottom + ScreenAdapter.getBottomNavHeight())
}
.width('100%')
.height('100%')
.backgroundColor('#F5F7FA')
}
@Builder
PlatformInfoCard() {
Column({ space: 12 }) {
Text('平台信息')
.fontSize(16)
.fontWeight(FontWeight.Medium)
#if defined(HARMONYOS)
Text('当前运行平台:HarmonyOS')
.fontSize(14)
.fontColor('#2196F3');
#elif defined(ANDROID)
Text('当前运行平台:Android')
.fontSize(14)
.fontColor('#4CAF50');
#elif defined(IOS)
Text('当前运行平台:iOS')
.fontSize(14)
.fontColor('#000000');
#else
Text('当前运行平台:Unknown')
.fontSize(14)
.fontColor('#F44336');
#endif
Text(`屏幕安全区:顶部${this.safeArea.top}px,底部${this.safeArea.bottom}px`)
.fontSize(12)
.fontColor('#888')
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
@Builder
FeatureCard() {
Column({ space: 12 }) {
Text('跨平台能力')
.fontSize(16)
.fontWeight(FontWeight.Medium)
// 这些组件在所有平台表现一致
Button('显示弹窗')
.onClick(() => {
AlertDialog.show({
title: '跨平台弹窗',
message: '这段代码在HarmonyOS、Android、iOS上都能运行',
primaryButton: { value: '确定', action: () => {} }
});
})
Button('列表滚动')
.onClick(() => {
// List、Scroll、Swiper等组件跨平台行为一致
})
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
@Builder
PlatformSpecificCard() {
Column({ space: 12 }) {
Text('平台专属能力')
.fontSize(16)
.fontWeight(FontWeight.Medium)
#if defined(HARMONYOS)
Button('调用鸿蒙分布式能力')
.onClick(() => {
// 仅鸿蒙平台编译这段代码
this.useDistributedCapability();
})
#elif defined(ANDROID)
Button('调用Android原生Toast')
.onClick(() => {
// 仅Android平台编译这段代码
arkuiX.loadNativeModule('android').showToast('Hello Android');
})
#elif defined(IOS)
Button('调用iOS原生分享')
.onClick(() => {
// 仅iOS平台编译这段代码
arkuiX.loadNativeModule('ios').share(['Hello iOS']);
})
#endif
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(12)
}
private useDistributedCapability() {
// 鸿蒙分布式能力
console.info('使用鸿蒙分布式软总线');
}
}
四、跨平台数据持久化适配
4.1 统一存储接口
不同平台的持久化API不同,需要封装统一接口:
// entry/src/main/ets/utils/StorageAdapter.ets
export interface KeyValueStorage {
set(key: string, value: string): Promise<void>;
get(key: string): Promise<string | null>;
remove(key: string): Promise<void>;
clear(): Promise<void>;
}
// 鸿蒙实现(Preferences)
class HarmonyOSStorage implements KeyValueStorage {
private preferences: dataPreferences.Preferences | null = null;
async init(): Promise<void> {
this.preferences = await dataPreferences.getPreferences(
getContext(),
'app_storage'
);
}
async set(key: string, value: string): Promise<void> {
await this.preferences?.put(key, value);
await this.preferences?.flush();
}
async get(key: string): Promise<string | null> {
return await this.preferences?.get(key, null) ?? null;
}
async remove(key: string): Promise<void> {
await this.preferences?.delete(key);
await this.preferences?.flush();
}
async clear(): Promise<void> {
await this.preferences?.clear();
await this.preferences?.flush();
}
}
// Android/iOS实现(通过桥接调用原生SharedPreferences/UserDefaults)
class BridgeStorage implements KeyValueStorage {
private nativeStorage: any;
constructor() {
this.nativeStorage = arkuiX.loadNativeModule('storage');
}
async set(key: string, value: string): Promise<void> {
await this.nativeStorage.set(key, value);
}
async get(key: string): Promise<string | null> {
return await this.nativeStorage.get(key);
}
async remove(key: string): Promise<void> {
await this.nativeStorage.remove(key);
}
async clear(): Promise<void> {
await this.nativeStorage.clear();
}
}
// 工厂
export async function createStorage(): Promise<KeyValueStorage> {
#if defined(HARMONYOS)
const storage = new HarmonyOSStorage();
await storage.init();
return storage;
#else
return new BridgeStorage();
#endif
}
五、构建与发布
5.1 多平台构建命令
# 构建HarmonyOS HAP
dev eco build --product harmonyos
# 构建Android APK
arkui-x build --platform android
# 构建iOS IPA(需要Mac + Xcode)
arkui-x build --platform ios
# 同时构建所有平台
arkui-x build --platform all
5.2 平台特定配置
// .arkui-x/platform.json
{
"platforms": {
"harmonyos": {
"compileSdkVersion": 12,
"compatibleSdkVersion": 12,
"targetSdkVersion": 12
},
"android": {
"compileSdkVersion": 34,
"minSdkVersion": 26,
"targetSdkVersion": 34
},
"ios": {
"deploymentTarget": "14.0"
}
}
}
5.3 发布策略
六、ArkUI-X 的局限与选型建议
6.1 当前局限
6.2 选型建议
七、小结与下篇预告
本文打开了HarmonyOS的跨平台视野:
ArkUI-X架构:统一ArkTS源码,平台桥接映射到原生UI
条件编译:通过
#if defined()处理平台差异平台桥接:原生能力扩展的JNI/Native Module机制
跨平台适配:安全区域、屏幕尺寸、持久化存储的统一封装
构建发布:一套源码产出多平台包
ArkUI-X让鸿蒙开发者可以"一次学习,多处投放",但它不是银弹。跨平台始终意味着妥协——在一致性与平台特色之间,在开发效率与极致性能之间,需要清醒的权衡。
跨平台解决了"跑在哪"的问题,而软件质量的核心在于"跑得多稳"。随着项目规模扩大,手动测试的成本会指数级上升。第19篇将深入鸿蒙单元测试与自动化测试,讲解测试框架、Mock机制、UI自动化和CI/CD集成,帮你建立持续交付的质量防线。
参考阅读
本系列第17篇:《HiAI Foundation Kit端侧AI集成:模型推理与NLP能力调用》