本文同步发表于微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一、元服务工程创建

1.1 创建工程步骤

1.1.1 启动创建
  • 首次打开:选择 Create Project

  • 已有工程File > New > Create Project

1.1.2 选择模板

选择 Atomic Service(元服务) 开发,支持模板:

模板类型 适用设备 主要功能 说明
Empty Ability Phone、Tablet 基础 Hello World 功能 标准元服务开发模板
[CloudDev]Empty Ability 跨设备 端云一体化开发 集成云开发能力
Embeddable Ability 支持嵌入的应用 嵌入式运行 可被其他应用嵌入的元服务

注意:元服务不支持 Native 开发方式,无法选择 Native 工程模板。

1.1.3 账号登录
  • 华为开发者账号登录:正式开发必须登录

  • 访客模式:仅用于体验开发功能

    • 在真机运行时需在 app.json5 中补充真实存在的包名

1.1.4 APP ID 配置
  1. 选择 APP ID:从已登录账号下的 APP ID 列表中选择

  2. 注册新 APP ID:如未注册,点击 Register APP ID

    • 应用类型必须选择"元服务"

  3. 刷新列表:注册后点击 Refresh 显示新 APP ID

1.1.5 Bundle Name 规则
  • 固定格式com.atomicservice.[appid]

  • 自动生成:开发者无法手动修改

  • 命名规范:不符合规范的包名不会在列表中显示

1.1.6 工程配置
  • Project name:填写工程名称

  • 其他参数:保持默认设置

  • 点击 Finish:自动生成示例代码和资源

1.2 元服务工程目录结构

元服务工程/
├── AppScope/
│   └── app.json5                 # 元服务全局配置信息
├── entry/                        # HarmonyOS工程模块(编译生成HAP)
│   ├── src/
│   │   ├── main/
│   │   │   ├── ets/
│   │   │   │   ├── entryability/  # 元服务入口
│   │   │   │   └── pages/        # 元服务页面
│   │   │   ├── resources/        # 资源文件(图形、字符串、布局等)
│   │   │   └── module.json5      # 模块配置文件
│   │   └── build-profile.json5  # 模块级编译配置
│   └── hvigorfile.ts            # 模块级构建脚本
├── oh_modules/                  # 三方库依赖信息
├── build-profile.json5         # 工程级配置(签名、产品配置)
└── hvigorfile.ts              # 工程级构建脚本
1.2.1 文件说明
  • app.json5:元服务的全局配置信息

  • module.json5:模块配置文件,包含HAP配置、设备配置等

  • build-profile.json5:编译构建配置

  • hvigorfile.ts:编译构建任务脚本,支持自定义

二、构建元服务页面

2.1 页面基本结构

2.1.1 默认页面(Index.ets)
import { hilog } from '@kit.PerformanceAnalysisKit';
import { router } from '@kit.ArkUI';

@Entry
@Component
struct MainPage {
  @State welcomeText: string = '欢迎使用';
  
  build() {
    Row() {
      Column() {
        // 文本组件
        Text(this.welcomeText)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .fontColor('#333333')
          
        // 按钮组件
        Button('进入详情')
          .type(ButtonType.Capsule)
          .margin({ top: 40 })
          .backgroundColor('#007DFF')
          .width('60%')
          .height(55)
          .fontSize(20)
          .fontWeight(FontWeight.Medium)
          .onClick(() => {
            // 页面跳转逻辑
            router.pushUrl({ url: 'pages/DetailPage' });
          })
      }
      .width('100%')
    }
    .height('100%')
    .backgroundColor('#F5F5F5')
  }
  
  aboutToAppear() {
    // 页面显示时的初始化逻辑
    hilog.info(0x0000, 'pageTag', '主页面准备显示');
  }
}
2.1.2 布局组件选择
布局场景 推荐组件 特点
线性排列 RowColumn 简单直观的线性布局
复杂对齐 RelativeContainer 灵活的相对位置控制
层叠显示 Stack 元素重叠显示
网格布局 Grid 规整的网格排列

2.2 创建第二个页面

2.2.1 新建页面文件
  1. 右键 pages 文件夹 → New > ArkTS File

  2. 输入文件名:如 DetailPage

  3. 自动生成文件结构

快捷方式:选择 New > Page 可自动配置路由

2.2.2 配置页面路由

main_pages.json 配置:

{
  "src": [
    "pages/MainPage",
    "pages/DetailPage",
    "pages/SettingPage"  // 可继续添加更多页面
  ]
}
2.2.3 第二个页面实现
import { router } from '@kit.ArkUI';

@Entry
@Component
struct DetailPage {
  @State detailInfo: string = '详情页面';
  @State dataCount: number = 0;
  
  build() {
    Row() {
      Column({ space: 15 }) {
        Text(this.detailInfo)
          .fontSize(45)
          .fontWeight(FontWeight.Bold)
          .fontColor('#1A1A1A')
        
        Text(`数据数量: ${this.dataCount}`)
          .fontSize(18)
          .fontColor('#666666')
          .margin({ top: 10 })
        
        Button('增加数据')
          .type(ButtonType.Normal)
          .backgroundColor('#34C759')
          .width('70%')
          .height(48)
          .fontSize(18)
          .onClick(() => {
            this.dataCount++;
          })
        
        Button('返回首页')
          .type(ButtonType.Capsule)
          .margin({ top: 30 })
          .backgroundColor('#007DFF')
          .width('50%')
          .height(48)
          .fontSize(18)
          .onClick(() => {
            router.back();
          })
      }
      .width('100%')
      .padding(20)
    }
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
}

2.3 页面间跳转实现

2.3.1 导入路由模块
import { router } from '@kit.ArkUI';
2.3.2 页面跳转方法
// 1. 普通跳转(推入堆栈)
router.pushUrl({ 
  url: 'pages/DetailPage' 
});

// 2. 替换当前页面
router.replaceUrl({ 
  url: 'pages/NewPage' 
});

// 3. 跳转并清空堆栈
router.clearStackAndPushUrl({ 
  url: 'pages/HomePage' 
});

// 4. 带参数跳转
router.pushUrl({
  url: 'pages/DetailPage',
  params: {
    userId: 'user123',
    productId: 'p456',
    timestamp: Date.now()
  }
});

// 5. 返回上一页
router.back();

// 6. 返回指定页面
router.back({ 
  url: 'pages/MainPage' 
});
2.3.3 接收页面参数
@Entry
@Component
struct DetailPage {
  @State productId: string = '';
  @State userId: string = '';
  
  aboutToAppear() {
    // 获取传递的参数
    const params = router.getParams() as Record<string, any>;
    this.productId = params['productId'] || '';
    this.userId = params['userId'] || '';
    
    // 使用参数加载数据
    this.loadProductDetail(this.productId);
  }
  
  private loadProductDetail(id: string) {
    // 根据ID加载商品详情
    console.info(`加载商品 ${id} 的详情信息`);
  }
}

三、新建元服务卡片

3.1 创建卡片步骤

3.1.1 创建卡片
  1. 右键 entry 模块 → New > Service Widget > Dynamic Widget

  2. 选择模板Hello World 卡片模板

  3. 点击 Next 进入配置页面

3.1.2 卡片配置参数
配置项 说明 要求/限制
Service widget name 卡片名称 同应用内不能重复,仅含字母、数字、下划线
Display name 预览面板显示的名称 API 11+ Stage 工程支持
Description 卡片描述信息 可选
Language 开发语言 元服务只支持 ArkTS,不支持 JS
Support dimension 支持的卡片规格 可多选,首次创建生成 EntryCard 目录
Default dimension 默认卡片规格 下拉选择
Ability name 挂靠的 Form Ability 选择已有或新建
Module name 所属模块 默认当前模块
3.1.3 完成创建
  • 点击 Finish 完成卡片创建

  • 生成卡片文件和快照目录

3.2 卡片开发实现

3.2.1 基础卡片代码
@Entry
@Component
struct ServiceCard {
  @State scaleValue: number = 1.0
  @State cardTitle: string = '快捷入口'
  
  build() {
    Column({ space: 12 }) {
      // 卡片标题
      Text(this.cardTitle)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1A1A1A')
        .width('100%')
        .textAlign(TextAlign.Center)
      
      // 功能按钮
      Button('快速启动')
        .width('85%')
        .height(45)
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
        .fontSize(16)
        .onClick(() => {
          // 点击动画效果
          this.scaleValue = 1.15
        })
        .scale({ x: this.scaleValue, y: this.scaleValue })
        .animation({
          curve: Curve.EaseInOut,
          playMode: PlayMode.AlternateReverse,
          duration: 180,
          onFinish: () => {
            this.scaleValue = 1.0
          }
        })
      
      // 状态信息
      Text('点击上方按钮体验')
        .fontSize(12)
        .fontColor('#666666')
        .width('100%')
        .textAlign(TextAlign.Center)
        .margin({ top: 8 })
    }
    .padding(16)
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
    .borderRadius(16)
    .shadow({ radius: 8, color: '#1A000000', offsetX: 0, offsetY: 2 })
  }
}
3.2.2 卡片点击跳转到元服务
@Entry
@Component
struct ServiceCard {
  @State scaleValue: number = 1.0
  
  build() {
    Column({ space: 12 }) {
      // 卡片内容...
    }
    .onClick(() => {
      // 点击整个卡片跳转到元服务
      postCardAction(this, {
        "action": "router",
        "abilityName": "EntryAbility",
        "params": {
          "source": "card",
          "timestamp": Date.now(),
          "targetPage": "MainPage"
        }
      });
    })
  }
}
3.2.3 卡片动作处理
事件类型 触发目标 使用场景 核心限制
router 跳转至指定 UIAbility 页面跳转、参数传递 非系统应用仅支持同应用内跳转
message 通知 FormExtensionAbility 消息通知、卡片内容更新 需在静态卡片中使用 FormLink
call 后台运行 UIAbility 后台任务(如音乐播放、数据同步) 需单实例模式 + 后台权限
// 支持多种卡片动作类型
postCardAction(this, {
  "action": "router",           // 路由跳转
  "abilityName": "EntryAbility",
  "params": { /* 参数 */ }
});

postCardAction(this, {
  "action": "message",          // 发送消息
  "params": { "type": "refresh" }
});

postCardAction(this, {
  "action": "call",             // 调用服务
  "params": { "service": "update" }
});

3.3 卡片快照自定义

3.3.1 快照文件位置
EntryCard/
└── entry/
    └── base/
        └── snapshot/
            ├── widget-2x2.png    # 2x2规格快照
            ├── widget-2x4.png    # 2x4规格快照
            └── widget-4x4.png    # 4x4规格快照
3.3.2 替换快照图片
  1. 准备图片:符合尺寸要求的PNG图片

  2. 替换文件:覆盖对应规格的快照文件

  3. 保持命名:确保文件名一致

  4. 刷新预览:重新编译查看效果

3.4 卡片规格与适配

3.4.1 常用卡片规格
规格 尺寸(vp) 适用场景
2x2 120×120 小工具、快捷入口
2x4 120×255 信息展示、简单列表
4x4 255×255 详细视图、复杂交互
3.4.2 响应式卡片设计
@Entry
@Component
struct AdaptiveCard {
  @Prop cardSize: { width: number, height: number } = { width: 120, height: 120 }
  
  build() {
    // 根据卡片尺寸调整布局
    const isSmall = this.cardSize.width <= 120;
    
    Column() {
      if (isSmall) {
        // 小尺寸布局
        Text('简版')
          .fontSize(14)
      } else {
        // 大尺寸布局
        Column({ space: 8 }) {
          Text('完整版')
            .fontSize(18)
          Text('更多信息')
            .fontSize(12)
        }
      }
    }
  }
}

四、开发注意事项

4.1 账号与认证

// 华为账号静默登录示例(Index页面中)
private loginWithHuaweiID() {
  const loginRequest = new authentication.HuaweiIDProvider()
    .createLoginWithHuaweiIDRequest();
  
  loginRequest.forceLogin = false;  // 非强制登录
  
  const controller = new authentication.AuthenticationController();
  controller.executeRequest(loginRequest)
    .then((data) => {
      const response = data as authentication.LoginWithHuaweiIDResponse;
      const authCode = response.data?.authorizationCode;
      // 将 authCode 发送到后端换取 unionID
    })
    .catch((error: BusinessError) => {
      if (error.code === authentication.AuthenticationErrorCode.ACCOUNT_NOT_LOGGED_IN) {
        // 华为账号未登录,建议跳转到登录引导页
        console.warn('华为账号未登录');
      }
    });
}

审核要求:如果元服务有账号系统,必须提供华为账号登录选项

4.2 开发规范

  1. 语言限制:只支持 ArkTS,不支持 JS

  2. 模型限制:只支持 Stage 模型

  3. API限制:只能使用元服务 API 集

  4. 版本要求:需要 HarmonyOS NEXT 及以上系统

4.3 性能优化

  1. 卡片轻量化:避免复杂计算和大量数据

4.4 调试

  1. 预览器调试:使用预览器实时查看效果

  2. 真机测试:配置签名后在真机运行

五、完整开发流程

发布准备

  1. 完善应用信息:图标、名称、描述

  2. 配置签名证书:申请发布证书

  3. 测试兼容性:多设备、多场景测试

  4. 准备素材:截图、演示视频、文档

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐