目 录CONTENT

文章目录

从代码层面学习鸿蒙App应用模型

DevWiki
2024-09-11 / 0 评论 / 0 点赞 / 45 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于2024-09-10,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

根据鸿蒙官方的文档, 程序包的定义如下:

用户应用程序泛指运行在设备的操作系统之上,为用户提供特定服务的程序,简称“应用”。一个应用所对应的软件包文件,称为“应用程序包”。

当前系统提供了应用程序包开发、安装、查询、更新、卸载的管理机制,便于开发者开发和管理应用。同时,系统还屏蔽了不同的芯片平台的差异(包括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组件的派生类来实现。
  • WindowStage

    每个UIAbility实例都会与一个WindowStage类实例绑定,该类起到了应用进程内窗口管理器的作用。它包含一个主窗口。也就是说UIAbility实例通过WindowStage持有了一个主窗口,该主窗口为ArkUI提供了绘制区域。

  • Context

    在Stage模型上,Context及其派生类向开发者提供在运行期可以调用的各种资源和能力。UIAbility组件和各种ExtensionAbility组件的派生类都有各自不同的Context类,他们都继承自基类Context,但是各自又根据所属组件,提供不同的能力。

  • AbilityStage

    每个Entry类型或者Feature类型的HAP在运行期都有一个AbilityStage类实例,当HAP中的代码首次被加载到进程中的时候,系统会先创建AbilityStage实例。

2. 从代码理解Stage模型

最方便理解的方式就是创建一个默认工程看看有什么.

在使用DevEco创建项目工程的时候,可以选择创建的Module类型,默认创建一个带有 entry 类型的应用,

创建工程以后,可以再次添加Module,但是已经有了一个 entry类型的Module,再次添加只能添加 feature类型的Module:

image-20240531145803110

在之前的文章:鸿蒙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

image-20240531150940495

如果你是Windows电脑,按住Ctrl然后鼠标点击 UIAbility中的 Ability, 即可查看父类 UIAbility, 持有了一个 UIAbilityContext类型的 context属性.

export default class UIAbility extends Ability {
    context: UIAbilityContext;
    // ...   
}

同时 UIAbility 包含了各个声明周期及变化的回调函数:

image-20240531151331136

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文件,具体步骤如下。

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为myabilitystage。

  2. 在myabilitystage目录,右键选择“New > ArkTS File”,新建一个文件并命名为MyAbilityStage.ts。

  3. 打开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';
      }
    }
    
  4. module.json5配置文件中,通过配置 srcEntry 参数来指定模块对应的代码路径,以作为HAP加载的入口。

  5. {
      "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 {
    // 根据系统可用内存的变化情况,释放不必要的内存
  }
}

根据官方的步骤,我们就可以得到下图:

image-20240822150748767

3. 总结

梳理一下上述的内容, 类关系图如下(点击可查看大图):

image-20240822140816416

另外官方文档提到App的StartUp过程,如图:

img

由上图可以看出, 和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】欢迎访问收藏。

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin
博主关闭了所有页面的评论