根据鸿蒙官方的文档, 程序包的定义如下:
用户应用程序泛指运行在设备的操作系统之上,为用户提供特定服务的程序,简称“应用”。一个应用所对应的软件包文件,称为“应用程序包”。
当前系统提供了应用程序包开发、安装、查询、更新、卸载的管理机制,便于开发者开发和管理应用。同时,系统还屏蔽了不同的芯片平台的差异(包括x86/ARM,32位/64位等),应用程序包在不同的芯片平台都能够安装运行,这使得开发者可以聚焦于应用的功能实现。
鸿蒙系统的应用是支持多Module设计机制:
- 支持模块化开发: 一个应用通常会包含多种功能,将不同的功能特性按模块来划分和管理是一种良好的设计方式。在开发过程中,我们可以将每个功能模块作为一个独立的Module进行开发,Module中可以包含源代码、资源文件、第三方库、配置文件等,每一个Module可以独立编译,实现特定的功能。这种模块化、松耦合的应用管理方式有助于应用的开发、维护与扩展。
- 支持多设备适配: 一个应用往往需要适配多种设备类型,在采用多Module设计的应用中,每个Module都会标注所支持的设备类型。有些Module支持全部类型的设备,有些Module只支持某一种或几种型的设备(比如平板),那么在应用市场分发应用包时,也能够根据设备类型做精准的筛选和匹配,从而将不同的包合理的组合和部署到对应的设备上。
根据文档, Module按照使用场景可以分为两种类型:
- Ability类型的Module: 用于实现应用的功能和特性。每一个Ability类型的Module编译后,会生成一个以.hap为后缀的文件,我们称其为HAP(Harmony Ability Package)包。HAP包可以独立安装和运行,是应用安装的基本单位,一个应用中可以包含一个或多个HAP包,具体包含如下两种类型。
- entry类型的Module:应用的主模块,包含应用的入口界面、入口图标和主功能特性,编译后生成entry类型的HAP。每一个应用分发到同一类型的设备上的应用程序包,只能包含唯一一个entry类型的HAP。
- feature类型的Module:应用的动态特性模块,编译后生成feature类型的HAP。一个应用中可以包含一个或多个feature类型的HAP,也可以不包含。
- Library类型的Module: 用于实现代码和资源的共享。同一个Library类型的Module可以被其他的Module多次引用,合理地使用该类型的Module,能够降低开发和维护成本。Library类型的Module分为Static和Shared两种类型,编译后会生成共享包。
- Static Library:静态共享库。编译后会生成一个以.har为后缀的文件,即静态共享包HAR(Harmony Archive)。
- Shared Library:动态共享库。编译后会生成一个以.hsp为后缀的文件,即动态共享包HSP(Harmony Shared Package)。
注意: 应用程序可以只包含一个基础的entry包,也可以包含一个基础的entry包和多个功能性的feature包。
三者引用关系图:
1. Stage Module
根据开发文档, 鸿蒙官方力推Stage模型开发, 逐步舍弃FA模型开发。
使用新版的DevEco创建项目时,默认为 Stage类型的应用程序, 具体的结构参见:
鸿蒙APP开发学习01 - 项目结构说明-DevWiki
https://devwiki.net/2024/02/27/Harmony-app-dev-01-project-struct
下图展示了Stage模型中的基本概念。
-
UIAbility组件和ExtensionAbility组件
Stage模型提供UIAbility和ExtensionAbility两种类型的组件,这两种组件都有具体的类承载,支持面向对象的开发方式。
- UIAbility组件是一种包含UI的应用组件,主要用于和用户交互。例如,图库类应用可以在UIAbility组件中展示图片瀑布流,在用户选择某个图片后,在新的页面中展示图片的详细内容。同时用户可以通过返回键返回到瀑布流页面。UIAbility组件的生命周期只包含创建/销毁/前台/后台等状态,与显示相关的状态通过WindowStage的事件暴露给开发者。
- ExtensionAbility组件是一种面向特定场景的应用组件。开发者并不直接从ExtensionAbility组件派生,而是需要使用ExtensionAbility组件的派生类。目前ExtensionAbility组件有用于卡片场景的FormExtensionAbility,用于输入法场景的InputMethodExtensionAbility,用于闲时任务场景的WorkSchedulerExtensionAbility等多种派生类,这些派生类都是基于特定场景提供的。例如,用户在桌面创建应用的卡片,需要应用开发者从FormExtensionAbility派生,实现其中的回调函数,并在配置文件中配置该能力。ExtensionAbility组件的派生类实例由用户触发创建,并由系统管理生命周期。在Stage模型上,三方应用开发者不能开发自定义服务,而需要根据自身的业务场景通过ExtensionAbility组件的派生类来实现。
-
每个UIAbility实例都会与一个WindowStage类实例绑定,该类起到了应用进程内窗口管理器的作用。它包含一个主窗口。也就是说UIAbility实例通过WindowStage持有了一个主窗口,该主窗口为ArkUI提供了绘制区域。
-
在Stage模型上,Context及其派生类向开发者提供在运行期可以调用的各种资源和能力。UIAbility组件和各种ExtensionAbility组件的派生类都有各自不同的Context类,他们都继承自基类Context,但是各自又根据所属组件,提供不同的能力。
-
每个Entry类型或者Feature类型的HAP在运行期都有一个AbilityStage类实例,当HAP中的代码首次被加载到进程中的时候,系统会先创建AbilityStage实例。
2. 从代码理解Stage模型
最方便理解的方式就是创建一个默认工程看看有什么.
在使用DevEco创建项目工程的时候,可以选择创建的Module类型,默认创建一个带有 entry 类型的应用,
创建工程以后,可以再次添加Module,但是已经有了一个 entry类型的Module,再次添加只能添加 feature类型的Module:
在之前的文章:鸿蒙APP开发学习01 - 项目结构说明-DevWiki
https://devwiki.net/2024/02/27/Harmony-app-dev-01-project-struct
中说明了项目的配置文件用途,这里详细解读一下。
本文的项目代码在此:
Harmony/HMDemo - HMDemo - DevWiki Gitea
https://git.devwiki.net/Harmony/HMDemo
2.1 module.json5
创建完以后可以看到 /entry/src/main/
下有个module.json5 配置文件,如下:
{
"module": {
"name": "app",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "AppAbility",
"deviceTypes": [
"phone",
"tablet",
"2in1"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "AppAbility",
"srcEntry": "./ets/appability/AppAbility.ets",
"description": "$string:AppAbility_desc",
"icon": "$media:icon",
"label": "$string:AppAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{
"name" : "ohos.permission.INTERNET"
}
]
}
}
这里有个配置 "mainElement": "AppAbility",
即程序的主 Ability为 AppAbility
, 其路径为: "srcEntry": "./ets/appability/AppAbility.ets"
上面代码也可以看出:
- 一个 entry类型的 Module 有一个主入口 Ability,即
mainElement
配置项 - 一个 entry类型的 Module 可以有多个 Ability,即
abilities
配置项
2.1 UIAbility
打开 UIAbility的代码可以看到:
export default class AppAbility extends UIAbility {
constructor() {
super();
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability constructor');
}
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
windowStage.loadContent('pages/Index', (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) ?? '');
});
}
onWindowStageDestroy(): void {
// Main window is destroyed, release UI related resources
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
// Ability has brought to foreground
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
从代码中可以看到:
- AppAbility 继承于 UIAbility
- AppAbility 覆写了几个声明周期函数
- 在 onWindowStageCreate回调函数中 使用windowStage.loadContent加载了第一个页面 Index
如果你是Windows电脑,按住Ctrl然后鼠标点击 UIAbility
中的 Ability
, 即可查看父类 UIAbility
, 持有了一个 UIAbilityContext
类型的 context属性.
export default class UIAbility extends Ability {
context: UIAbilityContext;
// ...
}
同时 UIAbility 包含了各个声明周期及变化的回调函数:
UIAbility 继承了 Ability, 接着查看 Ability的代码:
export default class Ability {
onConfigurationUpdate(newConfig: Configuration): void;
onMemoryLevel(level: AbilityConstant.MemoryLevel): void;
}
包含了:
- onConfigurationUpdate: 配置变化的回调
- onMemoryLevel: 内存级别变化的回调
2.2 Context
在 UIAbility
中持有了一个UIAbilityContext类型的属性, 查看 UIAbilityContext
源码,包含了几个属性以及一系列的 操作Ability的能力函数:
export default class UIAbilityContext extends Context {
abilityInfo: AbilityInfo;
currentHapModuleInfo: HapModuleInfo;
config: Configuration;
windowStage: window.WindowStage;
startAbility(want: Want, callback: AsyncCallback<void>): void;
openLink(link: string, options?: OpenLinkOptions, callback?: AsyncCallback<AbilityResult>): Promise<void>;
moveAbilityToBackground(): Promise<void>;
showAbility(): Promise<void>;
hideAbility(): Promise<void>;
}
其中:
- AbilityInfo: 包含了 module.json5 中配置abilities下的对应Ability的信息, moduleName, name, label, labelId, description等等
export interface AbilityInfo {
readonly bundleName: string;
readonly moduleName: string;
readonly name: string;
readonly label: string;
readonly labelId: number;
readonly description: string;
readonly icon: string;
// 等等
}
- HapModuleInfo: 包含 module.json5 中的module级别的信息
export interface AbilityInfo {
readonly bundleName: string;
readonly moduleName: string;
readonly name: string;
readonly label: string;
readonly labelId: number;
readonly description: string;
readonly icon: string;
// 等等
}
- Configuration: 包含系统的一些配置,包括语言, 颜色模式,屏幕分辨率等
export interface AbilityInfo {
readonly bundleName: string;
readonly moduleName: string;
readonly name: string;
readonly label: string;
readonly labelId: number;
readonly description: string;
readonly icon: string;
// 等等
}
- WindowStage: 是一个创建 加载管理 页面的接口, 也是通过这个加载我们写的页面
interface WindowStage {
getMainWindow(): Promise<Window>;
createSubWindow(name: string): Promise<Window>;
getSubWindow(): Promise<Array<Window>>;
loadContent(path: string, storage: LocalStorage, callback: AsyncCallback<void>): void;
loadContentByName(name: string, storage: LocalStorage, callback: AsyncCallback<void>): void;
on(eventType: 'windowStageEvent', callback: Callback<WindowStageEventType>): void;
off(eventType: 'windowStageEvent', callback?: Callback<WindowStageEventType>): void;
// 等等
}
UIAbilityContext的父类为 Context,包含了资源管理实例, App信息属性, 一系列的路径属性,以及获取Application级别的 Context.
export default class Context extends BaseContext {
resourceManager: resmgr.ResourceManager; // 资源管理器
applicationInfo: ApplicationInfo; // App信息,对应项目 AppScope目录下的 app.json5
cacheDir: string;
tempDir: string;
filesDir: string;
databaseDir: string;
preferencesDir: string;
bundleCodeDir: string;
distributedFilesDir: string;
resourceDir: string;
cloudFileDir: string;
eventHub: EventHub; // 事件的监听 发送
area: contextConstant.AreaMode;
getApplicationContext(): ApplicationContext;
getGroupDir(dataGroupID: string, callback: AsyncCallback<string>): void;
}
Context的父类为 BaseContext, 只包含一个属性 stageMode: boolean;
是 FA Module 还是 Stage Module.
2.3 AbilityStage
到这里,可能你会疑惑, 介绍 Stage模型的 AbilityStage
在哪? 默认创建的工程为何没有?
根据官方的文档: AbilityStage组件容器-Stage模型应用组件-Stage模型开发指导-Ability Kit(程序框架服务)-应用框架 - 华为HarmonyOS开发者
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/abilitystage-V5
AbilityStage是一个Module级别的组件容器,应用的HAP在首次加载时会创建一个AbilityStage实例,可以对该Module进行初始化等操作。
AbilityStage与Module一一对应,即一个Module拥有一个AbilityStage。
DevEco Studio默认工程中未自动生成AbilityStage,如需要使用AbilityStage的能力,可以手动新建一个AbilityStage文件,具体步骤如下。
在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为myabilitystage。
在myabilitystage目录,右键选择“New > ArkTS File”,新建一个文件并命名为MyAbilityStage.ts。
打开MyAbilityStage.ts文件,导入AbilityStage的依赖包,自定义类继承AbilityStage并加上需要的生命周期回调,示例中增加了一个onCreate()生命周期回调。
import { AbilityStage, Want } from '@kit.AbilityKit'; export default class MyAbilityStage extends AbilityStage { onCreate(): void { // 应用的HAP在首次加载的时,为该Module初始化操作 } onAcceptWant(want: Want): string { // 仅specified模式下触发 return 'MyAbilityStage'; } }
在module.json5配置文件中,通过配置 srcEntry 参数来指定模块对应的代码路径,以作为HAP加载的入口。
{ "module": { "name": "entry", "type": "entry", "srcEntry": "./ets/myabilitystage/MyAbilityStage.ts", ... } }
AbilityStage拥有onCreate()生命周期回调和onAcceptWant()、onConfigurationUpdated()、onMemoryLevel()事件回调。
- onCreate()生命周期回调:在开始加载对应Module的第一个UIAbility实例之前会先创建AbilityStage,并在AbilityStage创建完成之后执行其onCreate()生命周期回调。AbilityStage模块提供在Module加载的时候,通知开发者,可以在此进行该Module的初始化(如资源预加载,线程创建等)能力。
- onAcceptWant()事件回调:UIAbility指定实例模式(specified)启动时候触发的事件回调,具体使用请参见UIAbility启动模式综述。
- onConfigurationUpdated()事件回调:当系统全局配置发生变更时触发的事件,系统语言、深浅色等,配置项目前均定义在Configuration类中。
- onMemoryLevel()事件回调:当系统调整内存时触发的事件。
应用被切换到后台时,系统会将在后台的应用保留在缓存中。即使应用处于缓存中,也会影响系统整体性能。当系统资源不足时,系统会通过多种方式从应用中回收内存,必要时会完全停止应用,从而释放内存用于执行关键任务。为了进一步保持系统内存的平衡,避免系统停止用户的应用进程,可以在AbilityStage中的onMemoryLevel()生命周期回调中订阅系统内存的变化情况,释放不必要的资源。
import { AbilityStage, AbilityConstant } from '@kit.AbilityKit'; export default class AppAbilityStage extends AbilityStage { onMemoryLevel(level: AbilityConstant.MemoryLevel): void { // 根据系统可用内存的变化情况,释放不必要的内存 } }
根据官方的步骤,我们就可以得到下图:
3. 总结
梳理一下上述的内容, 类关系图如下(点击可查看大图):
另外官方文档提到App的StartUp过程,如图:
由上图可以看出, 和Android的 App类型相似, Android具有一个 Application的子类作为入口, 而这里的 Application是一个虚拟概念:
- 1个应用APP(Application)只有一个应用级别上下文: ApplicationContext
- 1个应用APP(Application)可以持有多个HAP,即一个Application可以只有1到多个 HAP
- 1个HAP对应有一个 AbilityStage, 同时对应一个 AbilityStageContext
- 1个HAP可以持有1到多个UIAbility 同时对应有 UIAbilityContext并持有 WindowStage,通过WindowStage可以获取Window (MainWindow)
- 最后是 windowStage 加载内容(loadContent)进入Page, 一个HAP有1到多个Page
4.后记
以上就是对 鸿蒙APP 模型的梳理~
如果你觉得本文对你有帮助,麻烦点击【在看】按钮让更多的人看到。同时关注公众号获取微信群二维码加入群聊一起交流学习。
本文会同步更新至我的博客【https://devwiki.net】欢迎访问收藏。