Administrator
发布于 2026-09-01 / 46 阅读
0
0

鸿蒙开发系列(第18篇):ArkUI-X跨平台开发——一套代码多端运行实践

鸿蒙开发系列 · 专家篇

前言

经过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

Flutter

React Native

KMP

开发语言

ArkTS(TypeScript超集)

Dart

JavaScript/TypeScript

Kotlin

UI范式

声明式(类SwiftUI)

声明式

命令式+声明式

声明式(Compose)

渲染方式

平台原生组件映射

自绘引擎Skia

原生组件桥接

平台原生组件映射

性能

接近原生

接近原生

桥接开销

接近原生

鸿蒙支持

官方原生支持

社区支持

社区支持

社区支持

生态成熟度

成长中

成熟

成熟

成长中

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 发布策略

平台

发布渠道

包格式

审核周期

HarmonyOS

华为应用市场

.app

1-3个工作日

Android

华为应用市场、其他安卓市场

.apk/.aab

1-3个工作日

iOS

App Store

.ipa

1-7个工作日


六、ArkUI-X 的局限与选型建议

6.1 当前局限

局限

说明

应对

生态成熟度

第三方库远少于Flutter/RN

核心功能自研,边缘功能桥接原生

平台能力覆盖

部分HarmonyOS能力无法跨平台

条件编译 + 平台桥接

调试体验

多平台联调复杂

优先在HarmonyOS上开发,再适配其他平台

性能优化

跨平台层有开销

避免高频UI更新,复杂计算放原生

6.2 选型建议

场景

推荐方案

鸿蒙为主,Android/iOS为辅

ArkUI-X

三平台同等重要

Flutter或KMP+Compose Multiplatform

已有RN/Flutter项目

保持现有方案,单独加鸿蒙模块

纯鸿蒙生态应用

原生ArkTS,无需ArkUI-X


七、小结与下篇预告

本文打开了HarmonyOS的跨平台视野:

  1. ArkUI-X架构:统一ArkTS源码,平台桥接映射到原生UI

  2. 条件编译:通过#if defined()处理平台差异

  3. 平台桥接:原生能力扩展的JNI/Native Module机制

  4. 跨平台适配:安全区域、屏幕尺寸、持久化存储的统一封装

  5. 构建发布:一套源码产出多平台包

ArkUI-X让鸿蒙开发者可以"一次学习,多处投放",但它不是银弹。跨平台始终意味着妥协——在一致性与平台特色之间,在开发效率与极致性能之间,需要清醒的权衡。

跨平台解决了"跑在哪"的问题,而软件质量的核心在于"跑得多稳"。随着项目规模扩大,手动测试的成本会指数级上升。第19篇将深入鸿蒙单元测试与自动化测试,讲解测试框架、Mock机制、UI自动化和CI/CD集成,帮你建立持续交付的质量防线。


参考阅读


评论