重要说明
该文章基于API9的介绍。后续版本会有一些修改
前言
鸿蒙发展过程中出现过两个主要的应用模型
FA模型开发概述 类Web开发范式,目前已经不再主推- Stage模型开发概述 声明式开发范式,为多模块开发精心设计,是未来发展的主要模型
Stage模型应用程序包结构
开发并打包完成后的程序包结构如图
- 开发者通过DevEco Studio把应用程序编译为一个或者多个.hap后缀的文件,即HAP的概念
- 一个应用中的.hap文件合在一起称为一个Bundle,bundleName是应用的唯一标识
- 所有的HAP最终会编译到一个App Pack中(以.app为后缀的包文件),用于发布到应用市场
需要特别说明的是:在应用上架到应用市场时,需要把应用包含的所有.hap文件(即Bundle)打包为一个.app后缀的文件用于上架,这个.app文件称为App Pack(Application Package),其中同时包含了描述App Pack属性的pack.info文件;在云端(服务器)分发和终端设备安装时,都是以HAP为单位进行分发和安装的。
HAP
- HAP是HarmonyOS应用安装的基本单位,包含了编译后的代码、资源、三方库及配置文件
- 打包后的HAP包结构包括ets、libs、resources等文件夹和resources.index、module.json、pack.info等文件。
- ets目录用于存放应用代码编译后的字节码文件
- libs目录用于存放库文件。库文件是HarmonyOS应用依赖的第三方代码(.so二进制文件)。
- resources目录用于存放应用的资源文件(字符串、图片等)
- resources.index是资源索引表,由IDE编译工程时生成
- module.json是HAP的配置文件,内容由工程配置中的module.json5和app.json5组成,该文件是HAP中必不可少的文件。
- pack.info是Bundle中用于描述每个HAP属性的文件,由IDE工具生成Bundle包时自动生成
- 开发态的目录结构和打包后的目录结构很类似
Entry类型的HAP
- 应用的主模块,在module.json5配置文件中的type标签配置为“entry”类型。
- 在 同一个应用中,同一设备类型只支持一个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。
Feature类型的HAP
- 应用的动态特性模块,在module.json5配置文件中的type标签配置为“feature”类型。
- 一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含;
- Feature类型的HAP通常用于实现应用的特性功能,可以配置成按需下载安装,也可以配置成随Entry类型的HAP一起下载安装(参见module对象内部结构中的“deliveryWithInstall”)
Module
- 一个开发态的Module编译后生成一个部署态的HAP,Module和HAP一一对应
- Module是HarmonyOS应用/服务的基本功能单元,包含了源代码、资源文件、第三方库及应用/服务配置文件,每一个Module都可以独立进行编译和运行。一个DevEco Studio工程可以包含多个Module
- Module分为“Ability”和“Library”两种类型
- “Ability”类型的Module对应于编译后的HAP(Harmony Ability Package);
- “Library”类型的Module对应于HAR 静态共享包(Harmony Archive),或者HSP动态共享包(Harmony Shared Package)
HAR相当于iOS中的的静态库,HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝
HSP相当于iOS中的动态库,HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份
- 一个Module可以包含一个或多个UIAbility组件
- Ability组件是一种包含用户界面的应用组件,用于与用户交互
- Ability组件是 系统调度的基本单元,为应用提供绘制界面的窗口
- 一个Ability组件中可以通过多个页面来实现一个模块功能
建议将不同模块功能拆解为不同的Ability组件单独实现,即将一个独立的功能模块放到一个Ability组件中,以多页面的形式呈现。每一个Ability组件实例,都对应于一个任务,可以在最近任务列表中呈现
鸿蒙支持快速修复包
快速修复包结构
appqf(Application Quick Fix)
- appqf与应用的app pack包是一一对应关系
- appqf包是HarmonyOS应用用于发布到应用市场的单元,不能够直接安装到设备上
- 由一个或多个hqf组成,这些hqf包在应用市场会从appqf包中拆分出来,再被分发到具体的设备上
hqf(Harmony Ability Package Quick Fix)
- hqf包是修复HAP中问题的快速修复包,用于安装到设备上的快速修复单元
- 一个hqf可以包含.abc的快速修复文件,.so的快速修复文件和描述该包的配置文件
- .abc文件:应用中修改后的ts代码,编译后生成的字节码文件
- libs目录:存放.so库文件的差分文件,以.so.diff为后缀。区分的不同的系统cpu架构,例如arm平台、x86平台
- patch.json:用于描述hqf包版本信息的配置文件,由开发者填写
快速修复TS编译后的文件
- 原始应用编译时,生成.abc文件和.map文件。.abc是TS代码编译后的字节码文件,应用运行时使用该文件。.map文件是通过TS代码编译工具编译TS代码时生成的中间文件,记录有代码中的函数、类等信息。
- 修复问题后的应用编译时,根据上述的.map文件,结合当前的TS代码,得到差异部分,根据差异部分生成快速修复的.abc文件。该.abc文件也既是最终要放到hqf包中的快速修复文件
快速修复包的发布部署流程
应用配置文件概述(Stage模型)
应用模型解读
Stage模型
应用组件
AbilityStage组件容器
- AbilityStage是一个Module级别的组件容器,应用的HAP在首次加载时会创建一个AbilityStage实例,可以对该Module进行初始化等操作
- AbilityStage与Module一一对应,即一个Module拥有一个AbilityStage
- AbilityStage拥有onCreate()生命周期回调和onAcceptWant()、onConfigurationUpdated()、onMemoryLevel()事件回调
onCreate()生命周期回调
- 在开始加载对应Module的第一个UIAbility实例之前会先创建AbilityStage,并在AbilityStage创建完成之后执行其onCreate()生命周期回调
- AbilityStage模块提供在Module加载的时候,通知开发者,可以在此进行该Module的初始化(如资源预加载,线程创建等)能力
onAcceptWant()事件回调
UIAbility指定实例模式(specified)启动时候触发的事件回调
onConfigurationUpdated()事件回调
当系统全局配置发生变更时触发的事件,系统语言、深浅色等,配置项目前均定义在Configuration类中
onMemoryLevel()事件回调
当系统调整内存时触发的事件
应用上下文Context
- Context是应用中对象的上下文,其提供了应用的一些基础信息
- UIAbility组件和各种ExtensionAbility派生类组件都有各自不同的Context类。分别有基类Context、ApplicationContext、AbilityStageContext、UIAbilityContext、ExtensionContext、ServiceExtensionContext等Context
UIAbility组件
- 包含UI界面,提供展示UI的能力,主要用于和用户交互。详细介绍请参见UIAbility组件概述。
UIAbility组件生命周期
- 当用户打开、切换和返回到对应应用时,应用中的UIAbility实例会在其生命周期的不同状态之间转换。
- UIAbility类提供了一系列回调,通过这些回调可以知道当前UIAbility实例的某个状态发生改变,会经过UIAbility实例的创建和销毁,或者UIAbility实例发生了前后台的状态切换。
- UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态
Create状态
- Create状态为在应用加载过程中,UIAbility实例创建完成时触发,系统会调用onCreate()回调。可以在该回调中进行应用初始化操作
2. UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI界面加载、设置WindowStage的事件订阅,通过loadContent()方法设置应用要加载的页面并根据需要订阅WindowStage的事件(获焦/失焦、可见/不可见)
3. 在UIAbility实例销毁之前,会先进入onWindowStageDestroy()回调,可以在该回调中释放UI界面资源
Foreground和Background状态
- onForeground()回调,在UIAbility的UI界面可见之前,如UIAbility切换至前台时触发。可以在onForeground()回调中申请系统需要的资源,或者重新申请在onBackground()中释放的资源
- onBackground()回调,在UIAbility的UI界面完全不可见之后,如UIAbility切换至后台时候触发。可以在onBackground()回调中释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等
Destroy状态
Destroy状态在UIAbility实例销毁时触发。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作
UIAbility组件启动模式
- 每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。
- 系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例
- 在module.json5配置文件中的"launchType"字段配置为"singleton"
- 每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例
- 即在最近任务列表中可以看到有多个该类型的UIAbility实例
- 在module.json5配置文件中的"launchType"字段配置为"standard"
- 在UIAbility实例创建之前,允许开发者为该实例创建一个唯一的字符串Key
- 创建的UIAbility实例绑定Key之后,后续每次调用startAbility()方法时,都会询问应用使用哪个Key对应的UIAbility实例来响应startAbility()请求。通过AbilityStage的onAcceptWant实现
- 运行时由UIAbility内部业务决定是否创建多实例,如果匹配有该UIAbility实例的Key,则直接拉起与之绑定的UIAbility实例,否则创建一个新的UIAbility实例
- module.json5配置文件的"launchType"字段配置为"specified"
// MyAbilityState中,onAcceptWant回调
onAcceptWant(want: Want) {
// 仅specified模式下触发。每次返回相同的key,和singleton效果一样
return "MyAbilityStage";
}
UIAbility组件与UI的数据同步
基于HarmonyOS的应用模型,可以通过以下两种方式来实现UIAbility组件与UI之间的数据同步
- EventHub:基于发布订阅模式来实现,事件需要先订阅后发布,订阅者收到消息后进行处理。
- 在使用EventHub之前,首先需要获取EventHub对象。基类Context提供了EventHub对象
- globalThis:ArkTS引擎实例内部的一个全局对象,在ArkTS引擎实例内部都能访问
globalThis是ES中的一个全局顶级对象,引擎内的所有Abilities和Pages共享这一个对象,存在相互覆盖的问题,特别不建议使用这种方式
const kJumpAbilityEvent = "jumpAbility";
this.context.eventHub.on(kJumpAbilityEvent, this.jumpAbility);
UIAbility组件间交互(设备内)
- UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时,会涉及到启动特定的UIAbility,该UIAbility可以是应用内的其他UIAbility,也可以是其他应用的UIAbility
- 通过调用
startAbility并传递给它want参数启动其他UIAbility,如果期望其他Ability返回结果则可以使用startAbilityForResult
- want中如果传入了abilityName则进行显示跳转,否则进行隐式跳转
jumpAbility = (data: JumpInterface) => {
console.log("收到event", data);
this.context.startAbilityForResult({
moduleName: "MyFeatureAbility",
abilityName: "MyFeatureAbility"
}, (b) => {
console.log("跳转Ability结果 ", b);
});
}
组件内页面路由(router)
- HarmonyOS提供了Router模块来完成页面的路由。Router模块提供了两种跳转模式,分别是router.pushUrl()和router.replaceUrl()
- router.pushUrl():目标页不会替换当前页,而是压入页面栈,以通过返回键或者调用router.back()方法返回到当前页
- router.replaceUrl():目标页会替换当前页,并销毁当前页。这样可以释放当前页的资源,并且无法返回到当前页
- Router模块提供了两种实例模式,分别是Standard和Single。这两种模式决定了目标url是否会对应多个实例
- Standard:标准实例模式,也是默认情况下的实例模式。每次调用该方法都会新建一个目标页,并压入栈顶
- Single:单实例模式。即如果目标页的url在页面栈中已经存在同url页面,则离栈顶最近的同url页面会被移动到栈顶,并重新加载;如果目标页的url在页面栈中不存在同url页面,则按照标准模式跳转
- 无论是pushUrl还是replaceUrl,都可以在跳转的同时携带参数,参数存贮于params中。
- 页面路由同时提供两种形式的API,分别是Promise和Callback。我们可以在async函数里用await调用,也可以在同步函数里用Promise调用或Callback调用
页面路由url完整形式:'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
// 同步函数里 Promise形式调用
private customAction = {
message: "这是传入的State",
onButtonClick: () => {
router.pushUrl({
// '@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
url: '@bundle:com.example.business/MyHSP/ets/pages/Index',
params: {
message: 'hello,这是我传给你的消息'
}
}).then(() => {
console.log("push page success");
}).catch(err => {
console.error(`pushUrl failed, code is ${err.code}, message is ${err.message}`);
});
}
};
ExtensionAbility组件(服务卡片)
提供特定场景(如卡片、输入法)的扩展能力,满足更多的使用场景。详细介绍请参见ExtensionAbility组件。
FormExtensionAbility:FORM类型的ExtensionAbility组件,用于提供服务卡片场景相关能力
ArkTS服务卡片运行机制
服务卡片支持不同的规格尺寸,开发者可以根据展示的不同内容和布局效果,选用不同的卡片尺寸,支持的尺寸包括:12、22、24和44宫格 每个module最多可以配置16张服务卡片
- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置,当前仅系统应用可以作为卡片使用方。
- 卡片提供方:提供卡片显示内容的应用,控制卡片的显示内容、控件布局以及控件点击事件。
- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,提供formProvider接口能力,同时提供卡片对象的管理与使用以及卡片周期性刷新等能力。
- 卡片渲染服务:用于管理卡片渲染实例,渲染实例与卡片使用方上的卡片组件一一绑定。卡片渲染服务运行卡片页面代码widgets.abc进行渲染,并将渲染后的数据发送至卡片使用方对应的卡片组件
- 卡片使用方的每个卡片组件都对应了卡片渲染服务里的一个渲染实例
- 同一应用提供方的渲染实例运行在同一个虚拟机运行环境中
- 不同应用提供方的渲染实例运行在不同的虚拟机运行环境中
- 通过虚拟机运行环境隔离不同应用提供方卡片之间的资源与状态
进程模型
Stage模型有三类进程,是从系统总体资源占用考虑,希望由系统负责应用进程的创建和销毁。所以不支持应用自定义配置多进程,也不支持通过接口启动进程
主进程
开发者编写的UIAbility入口及其依赖的代码都在该进程中运行。它是由UIAbility组件的启动触发创建的。
ExtensionAbility进程
开发者编写的同一种类型的ExtensionAbility组件实例都会在同一个进程中运行。不同类型的ExtensionAbility组件实例则在不同的进程中运行。该类进程是由系统服务在特定场景下创建,并根据用户对特定场景的使用,决定其何时销毁。同时该类进程独立于主进程创建,并且不支持与主进程之间进行IPC通信
渲染进程
为了支持WebView的运行,每个应用只能创建一个Render进程用于运行WebView的渲染引擎。这个Render进程也是由系统负责创建和销毁
进程间通信
基于HarmonyOS的进程模型,系统提供了公共事件机制用于一对多的通信场景,公共事件发布者可能存在多个订阅者同时接收事件
线程模型
- ArkTS引擎实例的创建 一个进程可以运行多个应用组件实例,所有应用组件实例共享一个ArkTS引擎实例。
- 线程模型 ArkTS引擎实例在主线程上创建。
- 进程内支持对象共享
HarmonyOS应用中每个进程都会有一个主线程,主线程有如下职责:
- 执行UI绘制;
- 管理主线程的ArkTS引擎实例,使多个UIAbility组件能够运行在其之上;
- 管理其他线程(例如Worker线程)的ArkTS引擎实例,例如启动和终止其他线程;
- 分发交互事件;
- 处理应用代码的回调,包括事件处理和生命周期管理;
- 接收Worker线程发送的消息;
HarmonyOS可以使用Worker线程执行耗时操作,这些线程无法直接操作UI。Worker线程在主线程中创建,与主线程相互独立。最多可以创建8个Worker
线程间通信
线程间通信目前主要有Emitter和Worker两种方式,其中Emitter主要适用于线程间的事件同步, Worker主要用于新开一个线程执行耗时任务
Stage模型只提供了主线程和Worker线程,Emitter主要用于主线程内或者主线程和Worker线程的事件同步
应用配置文件
使用app.json5描述应用信息,module.json5描述HAP信息、应用组件信息
FAQ
鸿蒙使用什么语言开发
鸿蒙中的UIAbility相当于iOS中的App?
可以这么理解。但有以下的几个区别
- iOS中的App是进程级别的,鸿蒙中的所有UIAbility共享一个进程
- iOS中的App在任务列表中只有一项,鸿蒙中的一个UIAbility在任务列表中就是一项
一个鸿蒙应用可以有多个任务?
鸿蒙App可以有多个Module组成,一个Module可以包含多个Ability,一个Ability是鸿蒙任务列表中的一个任务。所以一个鸿蒙应用可以有N多任务。
iOS中的UIViewController相当于鸿蒙中的什么?
UIViewController只是一个带有控制器的View,它和鸿蒙中@Entry @Component装饰的自定义组件最为相似
UIView相当于鸿蒙中的什么?
UIView相当于鸿蒙中的@Component装饰的自定义组件