鸿蒙开发入门与实践
liduoan.efls Engineer

本文所有内容基于HarmonyOS开发者3.1/4.0版本配套的开发者文档,对应鸿蒙SDK API能力级别为API 9 Release

本文内容:

1、介绍鸿蒙应用特点

2、介绍鸿蒙应用开发基本概念

3、能阅读并理解简单的鸿蒙应用代码、服务卡片代码

4、介绍当前抖音服务卡片&小艺建议

背景【遥遥领先!】

9月25日,华为在秋季全场景发布会上正式对外宣布启动HarmonyOS NEXT计划,即鸿蒙原生应用全面启动。 这也意味着鸿蒙操作系统(HarmonyOS)迭代四代后完全启动原生应用的时机已经成熟,条件已经具备,开启移动应用生态的新篇章。

image

在这个未来环境的基础上,我们可以先前置了解一下如何进行鸿蒙原生开发。

鸿蒙特点

我们可以看到鸿蒙官方上的图:

image

  1. ArkUI:极简的UI信息语法、丰富的UI组件、以及实时界面预览工具,只需使用一套ArkTS API,就能在多个HarmonyOS设备上提供生动而流畅的用户界面体验。

image

  1. 元服务(原名原子化服务),是HarmonyOS提供的一种面向未来的服务提供方式。 有独立入口的(用户可通过点击方式直接触发) 免安装的(无需显式安装,由系统程序框架后台安装后即可使用) 可为用户提供一个或多个便捷服务的用户应用程序形态元服务**最主要的呈现形态就是鸿蒙服务卡片

其将元服务/应用的重要信息以卡片的形式展示在桌面,通过轻量交互行为实现服务直达。

其主要特点为:

  • 免安装,更轻量化地将服务带给用户
  • 一键服务直达,将用户感兴趣的内容前置、外显
  • 跨端转移,多终端设备间无缝流转
  • 情景智能卡片推荐,随心定制、更懂用户「小艺建议」
  1. 跨端流转:当多个设备通过分布式操作系统能够相互感知、进而整合成一个超级终端时,设备与设备之间就可以取长补短、相互帮助,为用户提供更加自然流畅的分布式体验。

image

基本开发概念

UI框架(开发语言)

HarmonyOS提供了一套UI开发框架,即方舟开发框架(ArkUI框架)

方舟开发框架针对不同目的和技术背景的开发者提供了两种开发范式

  • 基于ArkTS的声明式开发范式(简称“声明式开发范式”)
  • 兼容JS的类Web开发范式(简称“类Web开发范式”)

官方文档3.0版本中是支持Java语言的,但是最新版本不支持了,ide创建新工程也没有了java语言的选项。

开发范式名称语言生态UI更新方式适用场景适用人群声明式开发范式ArkTS语言数据驱动更新复杂度较大、团队合作度较高的程序移动系统应用开发人员、系统应用开发人员类Web开发范式JS语言数据驱动更新界面较为简单的程序应用和卡片Web前端开发人员

应用模型(开发框架)

应用模型是HarmonyOS为开发者提供的应用程序所需能力的抽象提炼,它提供了应用程序必备的组件和运行机制。

简单理解 应用模型就类似一个框架,开发者在这个框架里去书写代码。类比前端框架React、Vue。

HarmonyOS先后提供了两种应用模型:

  • FA(Feature Ability)模型: HarmonyOS API 7开始支持的模型,已经不再主推。FA模型开发可见FA模型开发概述
  • Stage模型: HarmonyOS API 9开始新增的模型,是目前主推且会长期演进的模型
    • Stage模型提供UIAbility和ExtensionAbility两种类型的组件,这两种组件都有具体的类承载,支持面向对象的开发方式。

    • UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。

      • 例如,图库类应用可以在UIAbility组件中展示图片瀑布流,在用户选择某个图片后,在新的页面中展示图片的详细内容。同时用户可以通过返回键返回到瀑布流页面。

      • UIAbility的生命周期只包含创建/销毁/前台/后台等状态,与显示相关的状态通过WindowStage的事件暴露给开发者。

    • ExtensionAbility组件是一种面向特定场景的应用组件。

      • FormExtensionAbility:FORM类型的ExtensionAbility组件,用于提供服务卡片场景相关能力。
      • WorkSchedulerExtensionAbility:WORK_SCHEDULER类型的ExtensionAbility组件,用于提供延迟任务注册、取消、查询的能力。

模型区别

项目 FA****模型 Stage模型
应用组件 1. 组件分类 PageAbility组件:包含UI界面,提供展示UI的能力。详细介绍请参见PageAbility组件概述。ServiceAbility组件:提供后台服务的能力,无UI界面。详细介绍请参见ServiceAbility组件概述。DataAbility组件:提供数据分享的能力,无UI界面。详细介绍请参见DataAbility组件概述。2. 开发方式通过导出匿名对象、固定入口文件的方式指定应用组件。开发者无法进行派生,不利于扩展能力。 1. 组件分类UIAbility组件:包含UI界面,提供展示UI的能力,主要用于和用户交互。详细介绍请参见UIAbility组件概述。ExtensionAbility组件:提供特定场景(如卡片、输入法)的扩展能力,满足更多的使用场景。详细介绍请参见ExtensionAbility组件。 2. 开发方式采用面向对象的方式,将应用组件以类接口的形式开放给开发者,可以进行派生,利于扩展能力。
进程模型 有两类进程:1. 主进程2. 渲染进程详细介绍请参见进程模型 有三类进程:1. 主进程2. ExtensionAbility进程3. 渲染进程详细介绍请参见进程模型
线程模型 1. ArkTS引擎实例的创建一个进程可以运行多个应用组件实例,每个应用组件实例运行在一个单独的ArkTS引擎实例中。 2. 线程模型每个ArkTS引擎实例都在一个单独线程(非主线程)上创建,主线程没有ArkTS引擎实例。3. 进程内对象共享:不支持。 1. ArkTS引擎实例的创建一个进程可以运行多个应用组件实例,所有应用组件实例共享一个ArkTS引擎实例。2. 线程模型ArkTS引擎实例在主线程上创建。3. 进程内对象共享:支持。
配置文件 使用config.json描述应用信息、HAP信息和应用组件信息。详细介绍请参见应用配置文件概述(FA模型) 使用app.json5描述应用信息,module.json5描述HAP信息、应用组件信息。详细介绍请参见应用配置文件概述(Stage模型)

包结构

基于Stage模型开发的应用,经编译打包后,其应用程序包结构如下图应用程序包结构(Stage模型)所示。

image HAP可分为Entry和Feature两种类型。Entry类型的HAP: 是应用的主模块,在module.json5配置文件中的type标签配置为“entry”类型。 在同一个应用中,同一设备类型只支持一个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。Feature类型的HAP: 是应用的动态特性模块,在module.json5配置文件中的type标签配置为“feature”类型。 一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含。 Feature类型的HAP通常用于实现应用的特性功能,可以配置成按需下载安装,也可以配置成随Entry类型的HAP一起下载安装。 imageimage

一个应用包含一个或者多个Module,可以在DevEco Studio工程中创建一个或者多个Module

Module分为“Ability”和“Library”两种类型。

  • “Ability”类型的Module对应于编译后的HAP(Harmony Ability Package)
  • “Library”类型的Module对应于HAR(Harmony Archive),或者HSP(Harmony Shared Package)。

一个Module可以包含一个或多个UIAbility组件,如下图所示。

Module与UIAbility组件关系示意图:

image

应用开发:待办清单

下面以后续鸿蒙主推的Stage模型+ArkTs技术栈去Hello World。

完成一个简单的UI效果和服务卡片设计。

暂时无法在飞书文档外展示此内容

前置基础

IDE配置

简单的配置就不说了,按照文档处理

创建工程后的目录为

image详细如下:AppScope > app.json5:应用的全局配置信息。entry:HarmonyOS工程模块,编译构建生成一个HAP包。src > main > ets:用于存放ArkTS源码。src > main > ets > entryability:应用/服务的入口。src > main > ets > entryformability:服务卡片的Ability。src > main > ets > pages:应用/服务包含的页面。src > main > resources:用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。关于资源文件,详见资源分类与访问src > main > module.json5:Stage模型模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明,详见module.json5配置文件oh_modules:用于存放三方库依赖信息。关于原npm工程适配ohpm操作,请参考历史工程迁移build-profile.json5:应用级配置信息,包括签名、产品配置等。

ArkTS简单入门

ArkTS是HarmonyOS优选的主力应用开发语言。

它在TypeScript(简称TS)的基础上,匹配ArkUI框架,扩展了声明式UI、状态管理等相应的能力,让开发者以更简洁、更自然的方式开发跨端应用。要了解什么是ArkTS,我们首先要了解下ArkTS、TypeScript和JavaScript之间的关系:

  • JavaScript是一种属于网络的高级脚本语言,已经被广泛用于Web应用开发,常用来为网页添加各式各样的动态功能,为用户提供更流畅美观的浏览效果。
  • TypeScript 是 JavaScript 的一个超集,它扩展了 JavaScript 的语法,通过在JavaScript的基础上添加静态类型定义构建而成,是一个开源的编程语言。
  • ArkTS兼容TypeScript语言,拓展了声明式UI、状态管理、并发任务等能力。

由此可知,TypeScript是JavaScript的超集,ArkTS则是TypeScript的超集,他们的关系如下图所示:

image

TypeScript快速入门:https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101667356568959645

声明式UI

在ArkUI中,UI显示的内容均为组件,由框架直接提供的称为系统组件,由开发者定义的称为自定义组件。

image

  • @Component:@Component装饰器仅能装饰struct关键字声明的数据结构。struct被@Component装饰后具备组件化的能力,需要实现build方法描述UI,一个struct只能被一个@Component装饰。
  • build()函数:build()函数用于定义自定义组件的声明式UI描述,自定义组件必须定义build()函数。
  • @Entry:@Entry装饰的自定义组件将作为UI页面的入口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// 入口UI
@Entry
@Component
struct ToDoListPage {
private totalTasks: Array<string> = [];

aboutToAppear() {
this.totalTasks = DataModel.getData();
}

build() {
// 列布局
Column({ space: CommonConstants.COLUMN_SPACE }) {
// 标题文案
Text($r('app.string.page_title'))
.fontSize($r('app.float.title_font_size'))
.fontWeight(FontWeight.Bold)
.lineHeight($r('app.float.title_font_height'))
.width(CommonConstants.TITLE_WIDTH)
.margin({
top: $r('app.float.title_margin_top'),
bottom: $r('app.float.title_margin_bottom')
})
.textAlign(TextAlign.Start)

// 遍历添加item元素
ForEach(this.totalTasks, (item: string) => {
ToDoItem({ content: item })
}, (item: string) => JSON.stringify(item))

// 一个按钮
Button() {
Text('跳转页面')
.fontSize(30)
.fontWeight(FontWeight.Bold)
}
.onClick(() => {
console.info(`Succeeded in clicking the 'Next' button.`)
// 跳转到第二页
router.pushUrl({ url: 'pages/NextPage' }).then(() => {
console.info('Succeeded in jumping to the second page.')
}).catch((err) => {
console.error(`Failed to jump to the second page.Code is ${err.code}, message is ${err.message}`)
})}
)
.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('40%')
.height('5%')
}
.width(CommonConstants.FULL_LENGTH)
.height(CommonConstants.FULL_LENGTH)
.backgroundColor($r('app.color.page_background'))
}
}

// 自定义组件
@Component
export default struct ToDoItem {
private content?: string;
// 数据驱动UI
// @State当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
@State isComplete: boolean = false;

@Builder labelIcon(icon: Resource) {
Image(icon)
.objectFit(ImageFit.Contain)
.width($r('app.float.checkbox_width'))
.height($r('app.float.checkbox_width'))
.margin($r('app.float.checkbox_margin'))
}

build() {
// 行布局
Row() {
// 切换label的样式
if (this.isComplete) {
this.labelIcon($r('app.media.ic_ok'));
} else {
this.labelIcon($r('app.media.ic_default'));
}
// 文案内容
Text(this.content)
.fontSize($r('app.float.item_font_size'))
.fontWeight(CommonConstants.FONT_WEIGHT)
// 切换文案的样式
.opacity(this.isComplete ? CommonConstants.OPACITY_COMPLETED : CommonConstants.OPACITY_DEFAULT)
.decoration({ type: this.isComplete ? TextDecorationType.LineThrough : TextDecorationType.None })
}
.borderRadius(CommonConstants.BORDER_RADIUS)
.backgroundColor($r('app.color.start_window_background'))
.width(CommonConstants.LIST_DEFAULT_WIDTH)
.height($r('app.float.list_item_height'))
.onClick(() => {
// 点击后设置数据
this.isComplete = !this.isComplete;
})
}
}

状态管理

组件状态管理装饰器用来管理组件中的状态,它们分别是:@State、@Prop、@Link。

  • @State装饰的变量是组件内部的状态数据,当这些状态数据被修改时,将会调用所在组件的build方法进行UI刷新。
  • @Prop与@State有相同的语义,但初始化方式不同。@Prop装饰的变量必须使用其父组件提供的@State变量进行初始化,允许组件内部修改@Prop变量,但更改不会通知给父组件,即@Prop属于单向数据绑定。
  • @Link装饰的变量可以和父组件的@State变量建立双向数据绑定,需要注意的是:@Link变量不能在组件内部进行初始化。
  • @Builder装饰的方法用于定义组件的声明式UI描述,在一个自定义组件内快速生成多个布局内容。

@State、@Prop、@Link三者关系如图所示:

image

简单UI界面开发

1、UIAbility应用组件

UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。

UIAbility组件是系统调度的基本单元,为应用提供绘制界面的窗口

一个UIAbility组件中可以通过多个页面来实现一个功能模块。

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
// 启动该应用组件后进入的页面为:ToDoListPage
windowStage.loadContent('pages/ToDoListPage', (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
});
}
}

2、ArkTs编写对应的页面

上述的声明式UI中编写了两个页面

3、设置新页面路由信息

main_page.json文件内,编写页面信息

1
2
3
4
5
6
7
8
{
"src": [
// 第一个页面
"pages/ToDoListPage",
// 第二个页面
"pages/NextPage"
]
}

服务卡片开发

imageimageimage

服务卡片,在体验上类似于widget的表现,但是鸿蒙系统做了些优化,比如多个卡片可以折叠在一块鸿蒙小艺建议可以显示应用的服务卡片等等。

基本概念

image

  • 卡片使用方:如上图中的桌面,显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
    • 应用图标:应用入口图标,点击后可拉起应用进程,图标内容不支持交互。
    • 卡片:具备不同规格大小的界面展示,卡片的内容可以进行交互,如实现按钮进行界面的刷新应用的跳转等。
  • 卡片提供方:包含卡片的应用,提供卡片的显示内容、控件布局以及控件点击处理逻辑。
    • FormExtensionAbility:卡片业务逻辑模块,提供卡片创建、销毁、刷新等生命周期回调。
    • 卡片页面:卡片UI模块,包含页面控件、布局、事件等显示和交互信息。

IDE创建卡片

卡片的开发目前相对简单,IDE快速创建即可。

imageimage

卡片Ability

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// 在入参want中可以取出卡片的唯一标识:formId
let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY];
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
let obj = {
'title': 'titleOnAddForm',
'detail': 'detailOnAddForm'
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}

onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
'title': 'titleOnUpdateForm',
'detail': 'detailOnUpdateForm'
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((err) => {
if (err) {
// 异常分支打印
console.error(`[EntryFormAbility] Failed to updateForm. Code: ${err.code}, message: ${err.message}`);
return;
}
});
}

onRemoveForm(formId) {
// Called to notify the form provider that a specified form has been destroyed.
// 当对应的卡片删除时触发的回调,入参是被删除的卡片ID
console.info('[EntryFormAbility] onRemoveForm');
}

}

卡片UI

imageimage

实现简单的点击旋转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Entry
@Component
struct WidgetCard {

/*
* The height percentage setting.
*/
readonly FULL_HEIGHT_PERCENT: string = '100%';
@State myWidth: number = 100;
@State myHeight: number = 50;
// 标志位,true和false分别对应一组myWidth、myHeight值
@State flag: boolean = false;

build() {
Column({ space: 10 }) {
Button("text")
.type(ButtonType.Normal)
.width(this.myWidth)
.height(this.myHeight)
.margin(20)
Button("area: click me")
.fontSize(12)
.margin(20)
.onClick(() => {
animateTo({ duration: 1000, curve: Curve.Ease }, () => {
// 动画闭包中根据标志位改变控制第一个Button宽高的状态变量,使第一个Button做宽高动画
if (this.flag) {
this.myWidth = 100;
this.myHeight = 50;
} else {
this.myWidth = 200;
this.myHeight = 100;
}
this.flag = !this.flag;
});
})
}
.width("100%")
.height("100%")
.onClick(() => {
postCardAction(this, {
"action": 'router',
"abilityName": 'EntryAbility',
"params": {
"message": 'add detail'
}
});
})
}
}

卡片数据交互

image

  • 系统刷新后,调用FormExtensionAbility去刷新widget。
  • 业务逻辑主动更改卡片数据。

元服务

元服务和应用开发的差别主要在于是否免安装。

1
"installationFree": false

主要通过该参数来确定应用是否免安装。

目前经过测试发现

Stage应用模型不支持修改该参数

FA应用模型支持修改该参数将正常应用更改为不需安装的元服务应用。

https://developer.huawei.com/consumer/cn/doc/distribution/service/fa-dev-process-0000001491675000

参考文档:

官方文档:https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/start-overview-0000001478061421-V3?catalogVersion=V3

鸿蒙开发者文档:https://developer.huawei.com/consumer/cn/ HelloWorld:https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101667303102887820?ha_linker=eyJ0cyI6MTY5NTgwNDc2MTUyMiwiaWQiOiI3Yzk1MzQzNTk0ZTJhMjUzNTZjOWYyNzQ0MmI5Mjk5OSJ9

分布式文件服务:https://bbs.huaweicloud.com/blogs/381553

系统能力API文档:https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/syscap-0000001408089368-V3?catalogVersion=V3

暂时无法在飞书文档外展示此内容