HarmonyOS NEXT入门

基础入门

ArkTS工程目录结构(Stage模型)

  • AppScope > app.json5:应用的全局配置信息,详见app.json5配置文件。【不能放ets文件。模块的ets中引用它编译通不过无法预览但可以运行。可在资源文件中引用AppScope中的资源】
  • entry:HarmonyOS工程模块,编译构建生成一个HAP包。
    • src > main > ets:用于存放ArkTS源码。
    • src > main > ets > entryability:应用/服务的入口。
    • src > main > ets > entrybackupability:应用提供扩展的备份恢复能力。
    • src > main > ets > pages:应用/服务包含的页面。
    • src > main > resources:用于存放应用/服务所用到的资源文件,如图形、多媒体、字符串、布局文件等。关于资源文件,详见资源分类与访问
    • src > main > module.json5:模块配置文件。主要包含HAP包的配置信息、应用/服务在具体设备上的配置信息以及应用/服务的全局配置信息。具体的配置文件说明,详见module.json5配置文件
    • build-profile.json5:当前的模块信息 、编译信息配置项,包括buildOption、targets配置等。【不同字段模块级的buildOption配置会继承工程级配置】
    • hvigorfile.ts:模块级编译构建任务脚本。
    • obfuscation-rules.txt:混淆规则文件。混淆开启后,在使用Release模式进行编译时,会对代码进行编译、混淆及压缩处理,保护代码资产。详见开启代码混淆
    • oh-package.json5:用来描述包名、版本、入口文件(类型声明文件)和依赖项等信息。
  • oh_modules:用于存放三方库依赖信息。
  • build-profile.json5:工程级配置信息,包括签名signingConfigs、产品配置products等。其中products中可配置当前运行环境,默认为HarmonyOS。
  • hvigorfile.ts:工程级编译构建任务脚本。
  • oh-package.json5:主要用来描述全局配置,如:依赖覆盖(overrides)、依赖关系重写(overrideDependencyMap)和参数化配置(parameterFile)等。

开发基础知识

应用程序包基础知识

应用程序包概述-Module类型

  • Alibity类型的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)。

      实际上,Shared Library编译后除了会生成一个.hsp文件,还会生成一个.har文件。这个.har文件中包含了HSP对外导出的接口,应用中的其他模块需要通过.har文件来引用HSP的功能。为了表述方便,我们通常认为Shared Library编译后生成HSP。

共享包类型 编译和运行方式 发布和引用方式
HAR HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝。 HAR除了支持应用内引用,还可以独立打包发布,供其他应用引用。
HSP HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份 HSP一般随应用进行打包,当前支持应用内和集成态HSP。应用内HSP只支持应用内引用,集成态HSP支持发布到ohpm私仓和跨应用引用。

HAR和HSP在APP包中的形态示意图

应用程序包结构-Stage模型应用程序包结构

开发态包结构

在DevEco Studio上创建一个项目工程,并尝试创建多个不同类型的Module。根据实际工程中的目录对照本章节进行学习,可以有助于理解开发态的应用程序结构。

图1 项目工程结构示意图(以实际为准)
项目工程结构示意图

工程结构主要包含的文件类型及用途如下:

[!说明]

  • AppScope目录由DevEco Studio==自动生成==,不可更改。
  • Module目录名称可以由DevEco Studio自动生成(比如entry、library等),也可以自定义。为了便于说明,下表中统一采用Module_name表示。
文件类型 说明
配置文件 包括应用级配置信息、以及Module级配置信息:
- AppScope > app.json5app.json5配置文件,用于声明应用的全局配置信息,比如应用Bundle名称、应用名称、应用图标、应用版本号等。
- Module_name > src > main > module.json5module.json5配置文件,用于声明Module基本信息、支持的设备类型、所含的组件信息、运行所需申请的权限等。
ArkTS源码文件 Module_name > src > main > ets:用于存放Module的ArkTS源码文件(.ets文件)。
资源文件 包括应用级资源文件、以及Module级资源文件,支持图形、多媒体、字符串、布局文件等,详见资源分类与访问
- AppScope > resources :用于存放应用需要用到的资源文件。
- Module_name > src > main > resources :用于存放该Module需要用到的资源文件。
其他配置文件 用于编译构建,包括构建配置文件、编译构建任务脚本、混淆规则文件、依赖的共享包信息等。
- build-profile.json5:工程级或Module级的构建配置文件,包括应用签名、产品配置等。
- hvigorfile.ts:应用级或Module级的编译构建任务脚本,开发者可以自定义编译构建工具版本、控制构建行为的配置参数。
- obfuscation-rules.txt:混淆规则文件。混淆开启后,在使用Release模式进行编译时,会对代码进行编译、混淆及压缩处理,保护代码资产。
- oh-package.json5:用于存放依赖库的信息,包括所依赖的三方库和共享包。
编译态包结构

不同类型的Module编译后会生成对应的HAP、HAR、HSP等文件,开发态视图与编译态视图的对照关系如下:
图2 开发态与编译态的工程结构视图
开发态与编译态的工程结构视图

从开发态到编译态,Module中的文件会发生如下变更:

  • ets目录:ArkTS源码编译生成.abc文件。
  • resources目录:AppScope目录下的资源文件会合入到Module下面资源目录中,如果两个目录下的存在重名文件,编译打包后只会保留AppScope目录下的资源文件。
  • module配置文件:AppScope目录下的app.json5文件字段会合入到Module下面的module.json5文件之中,编译后生成HAP或HSP最终的module.json文件。

[!说明]
在编译HAP和HSP时,会把他们==所依赖的HAR直接编译到HAP和HSP中==。

发布态包结构

每个应用中至少包含一个.hap文件,可能包含若干个.hsp文件、也可能不含,一个应用中的所有.hap.hsp文件合在一起称为Bundle,其对应的bundleName是应用的唯一标识(详见app.json5配置文件中的bundleName标签)。
当应用发布上架到应用市场时,需要将Bundle打包为一个.app后缀的文件用于上架,这个.app文件称为App Pack(Application Package),与此同时,DevEco Studio工具自动会生成一个pack.info文件。pack.info文件描述了App Pack中每个HAP和HSP的属性,包含APP中的bundleName和versionCode信息、以及Module中的name、type和abilities等信息。

[!说明]

  • App Pack是发布上架到应用市场的基本单元,但是==不能在设备上直接安装和运行==。
  • 在应用签名、云端分发、端侧安装时,都是==以HAP/HSP为单位==进行签名、分发和安装的。

图3 编译发布与上架部署流程图
编译发布与上架部署流程图

选择合适的包类型

HAP、HAR、HSP三者的功能和使用场景总结对比如下:

Module类型 包类型 说明
Ability HAP 应用的功能模块,可以独立安装和运行,必须包含一个entry类型的HAP,可选包含一个或多个feature类型的HAP。
Static Library HAR 静态共享包,==编译态复用==。
- 支持应用内共享,也可以发布后供其他应用使用。
- 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。
- 作为三方库,发布到OHPM中心仓,供其他应用使用。
- 多包(HAP/HSP)引用相同的HAR时,会造成多包间代码和资源的重复拷贝,从而导致应用包膨大。
Shared Library HSP 动态共享包,==运行时复用==。
- 当前仅支持==应用内共享==。
- 当多包(HAP/HSP)同时引用同一个共享包时,采用HSP替代HAR,可以避免HAR造成的多包间代码和资源的重复拷贝,从而减小应用包大小。

HAP、HSP、HAR支持的规格对比如下,其中“√”表示是,“×”表示否。
开发者可以根据实际场景所需的能力,选择相应类型的包进行开发。在后续的章节中还会针对如何使用HAPHARHSP分别展开详细介绍。

规格 HAP HAR HSP
支持在配置文件中声明UIAbility组件【系统调度应用的最小单元】与ExtensionAbility组件 × ×
支持在配置文件中声明pages页面 ×
支持包含资源文件与.so文件
支持依赖其他HAR文件
支持依赖其他HSP文件
支持在设备上独立安装运行 × ×

[!说明]

  • HAR虽然不支持在配置文件中声明pages页面,但是可以包含pages页面,并通过命名路由的方式进行跳转。
  • 由于HSP==仅==支持应用内共享,如果HAR依赖了HSP,则该HAR文件仅支持应用内共享,不支持发布到二方仓或三方仓供其他应用使用,否则会导致编译失败。
  • HAR和HSP均==不支持循环依赖==,也==不支持依赖传递==。

应用程序包结构-FA模型应用程序包结构

基于FA模型开发的应用,其应用程序包结构如下图应用程序包结构(FA模型) 所示。开发者需要熟悉应用程序包结构相关的基本概念。

FA模型与Stage模型不同之处在于HAP内部文件存放位置不同,FA模型将所有的资源文件、库文件和代码文件都放在assets文件夹中,在文件夹内部进一步区分。

  • config.json是应用配置文件,IDE会自动生成一部分模块代码,开发者按需修改其中的配置。详细字段请参见应用配置文件
  • assets是HAP所有的资源文件、库文件和代码文件的集合,内部可以分为entryjs文件夹。entry文件夹中存放的是resources目录和resources.index文件。
  • resources目录用于存放应用的资源文件(字符串、图片等),便于开发者使用和维护,详见资源文件的使用
  • resources.index是资源索引表,由IDE调用SDK工具生成。
  • js文件夹中存放的是编译后的代码文件。
  • pack.info是Bundle中用于描述每个HAP属性的文件,例如app中的bundleName和versionCode信息、module中的name、type和abilities等信息,由IDE工具构建Bundle包时自动生成。

图1 应用程序包结构(FA模型)
应用程序包结构(FA模型)

应用程序包开发与使用-HAP

HAP(Harmony Ability Package)是应用安装和运行的基本单元。HAP包是由代码、资源、第三方库、配置文件等打包生成的模块包,其主要分为两种类型:entry和feature。

  • entry:应用的主模块,作为应用的入口,提供了应用的基础功能。
  • feature:应用的动态特性模块,作为应用能力的扩展,可以根据用户的需求和设备类型进行选择性安装。
    应用程序包可以只包含一个基础的entry包,也可以包含一个基础的entry包和多个功能性的feature包。
    使用场景
  • 单HAP场景:如果只包含UIAbility组件,无需使用ExtensionAbility组件,优先采用单HAP(即一个entry包)来实现应用开发。虽然一个HAP中可以包含一个或多个UIAbility组件,为了避免不必要的资源加载,推荐采用“一个UIAbility+多个页面”的方式。
  • 多HAP场景:如果应用的功能比较复杂,需要使用ExtensionAbility组件,可以采用多HAP(即一个entry包+多个feature包)来实现应用开发,每个HAP中包含一个UIAbility组件或者一个ExtensionAbility组件。在这种场景下,可能会存在多个HAP引用相同的库文件,导致重复打包的问题。
    约束限制
  • 不支持导出接口和ArkUI组件,给其他模块使用。
  • 多HAP场景下,App Pack包中同一设备类型的所有HAP中必须有且只有一个Entry类型的HAP,Feature类型的HAP可以有一个或者多个,也可以没有。
  • 多HAP场景下,同一应用中的所有HAP的配置文件中的bundleName、versionCode、versionName、minCompatibleVersionCode、debug、minAPIVersion、targetAPIVersion、apiReleaseType相同,同一设备类型的所有HAP对应的moduleName标签必须唯一。HAP打包生成App Pack包时,会对上述参数配置进行校验。
  • 多HAP场景下,同一应用的所有HAP、HSP的签名证书要保持一致。上架应用市场是以App Pack形式上架,应用市场分发时会将所有HAP从App Pack中拆分出来,同时对其中的所有HAP进行重签名,这样保证了所有HAP签名证书的一致性。在调试阶段,开发者通过命令行或DevEco Studio将HAP安装到设备上时,要保证所有HAP签名证书一致,否则会出现安装失败的问题。
    创建
    下面简要介绍如何通过DevEco Studio新建一个HAP模块。
  1. 创建工程,详见构建第一个ArkTS应用
  2. 在工程目录上单击右键,选择New > Module
  3. 在弹出的对话框中选择Empty Ability模板,单击Next
  4. 在Module配置界面,配置Module name,选择Module TypeDevice Type,然后单击Next
  5. 在Ability配置界面,配置Ability name,然后单击Finish完成创建。
    开发
  • HAP中支持添加UIAbility组件或ExtensionAbility组件,添加pages页面。具体操作可参考应用/服务开发
  • HAP中支持引用HAR或HSP共享包,详见HAR的使用HSP的使用
    调试
    通过DevEco Studio编译打包,生成单个或者多个HAP,即可基于HAP进行调试。如需根据不同的部署环境、目标人群、运行环境等,将同一个HAP定制编译为不同版本,请参见定制编译指导
    开发者可以采用DevEco Studio或者hdc工具进行调试:
  • 方法一: 使用DevEco Studio进行调试,详见应用程序包调试方法
  • 方法二: 使用hdc工具(可通过HarmonyOS SDK获取,在SDK的toolchains目录下)进行调试。
    在调试前,需要先安装或更新HAP,此处有两种方式:
    • 直接使用hdc安装、更新HAP。
      HAP的路径为开发平台上的文件路径,以Windows开发平台为例,命令参考如下:

      1
      2
      3
      4
      5
      6
      7
      8
             // 安装、更新,多HAP可以指定多个文件路径
      hdc install entry.hap feature.hap
      // 执行结果
      install bundle successfully.
      // 卸载
      hdc uninstall com.example.myapplication
      // 执行结果
      uninstall bundle successfully.

    • 先执行hdc shell,再使用bm工具安装、更新HAP。
      HAP的文件路径为真机上的文件路径,命令参考如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
             // 先执行hdc shell才能使用bm工具
      hdc shell
      // 安装、更新,多HAP可以指定多个文件路径
      bm install -p /data/app/entry.hap /data/app/feature.hap
      // 执行结果
      install bundle successfully.
      // 卸载
      bm uninstall -n com.example.myapplication
      // 执行结果
      uninstall bundle successfully.

      完成HAP安装或更新后,即可参考相关调试命令进行 调试

应用程序包开发与使用-HAR

HAR(Harmony Archive)是静态共享包,可以包含代码、C++库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。

使用场景
  • 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。
  • 作为三方库,发布到OHPM中心仓,供其他应用使用。
    约束限制
  • HAR不支持在设备上单独安装/运行,只能作为应用模块的依赖项被引用。 ^1bb0e7
  • HAR不支持在配置文件中声明UIAbility组件与ExtensionAbility组件。
  • HAR不支持在配置文件中声明pages页面,但是可以包含pages页面,并通过命名路由的方式进行跳转。
  • HAR不支持引用AppScope目录中的资源。在编译构建时,AppScope中的内容不会打包到HAR中,因此会导致HAR资源引用失败。
  • HAR可以依赖其他HAR,但不支持循环依赖,也不支持依赖传递。
    创建
    通过DevEco Studio创建一个HAR模块,详见创建库模块
    开发
    介绍如何导出HAR的ArkUI组件、接口、资源,供其他应用或当前应用的其他模块引用。
    Index.ets文件是HAR导出声明文件的入口,HAR需要导出的接口,统一在Index.ets文件中导出。Index.ets文件是DevEco Studio默认自动生成的,用户也可以自定义,在模块的oh-package.json5文件中的main字段配置入口声明文件,配置如下所示:
    1
    2
    3
    {
    "main": "Index.ets"
    }
    导出ArkUI组件
    ArkUI组件的导出方式与ts的导出方式一致,通过==export==导出ArkUI组件,示例如下:
    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
    // library/src/main/ets/components/mainpage/MainPage.ets
    @Component
    export struct MainPage {
    @State message: string = 'HAR MainPage';

    build() {
    Column() {
    Row() {
    Text(this.message)
    .fontSize(32)
    .fontWeight(FontWeight.Bold)
    }
    .margin({ top: '32px' })
    .height(56)
    .width('624px')

    Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center, alignContent: FlexAlign.Center }) {
    Column() {
    Image($r('app.media.pic_empty')).width('33%')
    Text($r('app.string.empty'))
    .fontSize(14)
    .fontColor($r('app.color.text_color'))
    }
    }.width('100%')
    .height('90%')
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.page_background'))
    }
    }
    HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:
    1
    2
    // library/Index.ets
    export { MainPage } from './src/main/ets/components/mainpage/MainPage';
    导出ts类和方法
    通过export导出ts类和方法,支持导出多个ts类和方法,示例如下所示:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // library/src/main/ts/test.ets
    export class Log {
    static info(msg: string) {
    console.info(msg);
    }
    }

    export function func() {
    return 'har func';
    }

    export function func2() {
    return 'har func2';
    }
    HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:
    1
    2
    3
    4
    // library/Index.ets
    export { Log } from './src/main/ts/test';
    export { func } from './src/main/ts/test';
    export { func2 } from './src/main/ts/test';
    导出native方法
    在HAR中也可以包含C++编写的so。对于so中的native方法,HAR通过以下方式导出,以导出libnative.so的加法接口add为例:
    1
    2
    3
    4
    5
    6
    7
    // library/src/main/ets/utils/nativeTest.ts
    import native from 'liblibrary.so';

    export function nativeAdd(a: number, b: number): number {
    let result: number = native.add(a, b);
    return result;
    }
    HAR对外暴露的接口,在Index.ets导出文件中声明如下所示:
    1
    2
    // library/Index.ets
    export { nativeAdd } from './src/main/ets/utils/nativeTest';
    资源
    在编译构建HAP时,DevEco Studio会从HAP模块及依赖的模块中收集资源文件,如果不同模块下的==资源文件==出现重名==冲突==时,DevEco Studio会按照以下优先级进行==覆盖==(优先级由高到低):
  • AppScope(仅API9的Stage模型支持)。
  • HAP包自身模块。
  • 依赖的HAR模块,如果依赖的多个HAR之间有资源冲突,会按照工程oh-package.json5中dependencies下的依赖顺序进行覆盖,依赖顺序在前的优先级较高。例如下方示例中dayjs和lottie中包含同名文件时,会优先使用dayjs中的资源。

[!说明]
如果在AppScope/HAP模块/HAR模块的国际化目录中配置了资源,在相同的国际化限定词下,合并的优先级也遵循上述规则。同时,国际化限定词中配置的优先级高于在base中的配置。如:在AppScope的base中配置了资源字段,在HAR模块的en_US中配置了同样的资源字段,则在en_US的使用场景中,会更优先使用HAR模块中配置的资源字段。

1
2
3
4
5
6
7
// oh-package.json5
{
"dependencies": {
"dayjs": "^1.10.4",
"lottie": "^2.0.0"
}
}
使用

介绍如何配置HAR依赖,并引用HAR的ArkUI组件、接口、资源。
引用HAR前,需要先配置对HAR的依赖,详见引用HAR文件和资源

引用HAR的ArkUI组件

HAR的依赖配置成功后,可以引用HAR的ArkUI组件。ArkUI组件的导入方式与ts的导入方式一致,通过import引入HAR导出的ArkUI组件,示例如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// entry/src/main/ets/pages/IndexSec.ets
import { MainPage } from 'library';

@Entry
@Component
struct IndexSec {
build() {
Row() {
// 引用HAR的ArkUI组件
MainPage()
}
.height('100%')
}
}
引用HAR的ts类和方法

通过import引用HAR导出的ts类和方法,示例如下所示:

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
// entry/src/main/ets/pages/Index.ets
import { Log } from 'library';
import { func } from 'library';

@Entry
@Component
struct Index {
@State message: string = 'Hello World';

build() {
Column() {
Text(this.message)
.fontFamily('HarmonyHeiTi')
.fontWeight(FontWeight.Bold)
.fontSize(32)
.fontWeight(700)
.fontColor($r('app.color.text_color'))
.textAlign(TextAlign.Start)
.margin({ top: '32px' })
.width('624px')

//引用HAR的ts类和方法
Button($r('app.string.button'))
.id('button')
.height(48)
.width('624px')
.margin({ top: '4%' })
.type(ButtonType.Capsule)
.fontFamily('HarmonyHeiTi')
.borderRadius($r('sys.float.ohos_id_corner_radius_button'))
.backgroundColor($r('app.color.button_background'))
.fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
.fontSize($r('sys.float.ohos_id_text_size_button1'))
.onClick(() => {
// 引用HAR的类和方法
Log.info('har msg');
this.message = 'func return: ' + func();
})
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
引用HAR的native方法

通过import引用HAR导出的native方法,示例如下所示:

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
// entry/src/main/ets/pages/Index.ets
import { nativeAdd } from 'library';

@Entry
@Component
struct Index {
@State message: string = 'Hello World';

build() {
Column() {
Text(this.message)
.fontFamily('HarmonyHeiTi')
.fontWeight(FontWeight.Bold)
.fontSize(32)
.fontWeight(700)
.fontColor($r('app.color.text_color'))
.textAlign(TextAlign.Start)
.margin({ top: '32px' })
.width('624px')

//引用HAR的native方法
Button($r('app.string.native_add'))
.id('nativeAdd')
.height(48)
.width('624px')
.margin({ top: '4%', bottom: '6%' })
.type(ButtonType.Capsule)
.fontFamily('HarmonyHeiTi')
.borderRadius($r('sys.float.ohos_id_corner_radius_button'))
.backgroundColor($r('app.color.button_background'))
.fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
.fontSize($r('sys.float.ohos_id_text_size_button1'))
.onClick(() => {
this.message = 'result: ' + nativeAdd(1, 2);
})
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
引用HAR的资源

通过==$r==引用HAR中的资源,例如在HAR模块的src/main/resources里添加字符串资源(在string.json中定义,name:hello_har)和图片资源(icon_har.png),然后在Entry模块中引用该字符串和图片资源的示例如下所示:

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
// entry/src/main/ets/pages/Index.ets
@Entry
@Component
struct Index {
@State message: string = 'Hello World';

build() {
Column() {
// 引用HAR的字符串资源
Text($r('app.string.hello_har'))
.id('stringHar')
.fontFamily('HarmonyHeiTi')
.fontColor($r('app.color.text_color'))
.fontSize(24)
.fontWeight(500)
.margin({ top: '40%' })

List() {
ListItem() {
// 引用HAR的图片资源
Image($r('app.media.icon_har'))
.id('iconHar')
.borderRadius('48px')
}
.margin({ top: '5%' })
.width('312px')
}
.alignListItem(ListItemAlign.Center)
}
.width('100%')
.backgroundColor($r('app.color.page_background'))
.height('100%')
}
}
编译

HAR可以作为二方库和三方库提供给其他应用使用,如果需要对代码资产进行保护时,建议开启混淆能力。
混淆能力开启后,DevEco Studio在构建HAR时,会对代码进行编译、混淆及压缩处理,保护代码资产。

[!说明]
仅Stage模型的ArkTS工程支持混淆。
HAR开启混淆后资源ID为-1,ResourceManager等通过ID获取资源的API不再生效。

对于API 10及以上版本,HAR模块默认开启混淆能力,可以在HAR模块的build-profile.json5文件中的ruleOptions字段下的enable进行设置,配置如下所示:

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
{
"apiType": "stageMode",
"buildOption": {
},
"buildOptionSet": [
{
"name": "release",
"arkOptions": {
"obfuscation": {
"ruleOptions": {
"enable": true,
"files": [
"./obfuscation-rules.txt"
]
},
"consumerFiles": [
"./consumer-rules.txt"
]
}
}
},
],
"targets": [
{
"name": "default"
}
]
}
编译生成TS文件

[!说明]
在har中使用Sendable class时,开启该配置。

har模块中arkts文件编译后,默认产物为js文件,想要将产物修改为ts文件,可以在har模块src/main目录下的module.json5文件中的”metadata”字段下的”UseTsHar”进行设置,配置如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"module": {
"name": "TsClosedHar",
"type": "har",
"deviceTypes": [
"default",
"tablet",
"2in1"
],
"metadata": [
{
"name": "UseTsHar",
"value": "true"
}
]
}
}
发布

详见发布HAR

应用程序包开发与使用-HSP

HSP(Harmony Shared Package)是动态共享包,可以包含代码、C++库、资源和配置文件,通过HSP可以实现代码和资源的共享。HSP不支持独立发布,而是跟随其宿主应用的APP包一起发布,与宿主应用同进程,具有相同的包名和生命周期。

[!说明]
应用内HSP:在编译过程中与应用包名(bundleName)强耦合,只能给某个特定的应用使用。
集成态HSP:构建、发布过程中,不与特定的应用包名耦合;使用时,工具链支持自动将集成态HSP的包名替换成宿主应用包名。

使用场景
  • 多个HAP/HSP共用的代码和资源放在同一个HSP中,可以提高代码、资源的可重用性和可维护性,同时编译打包时也只保留一份HSP代码和资源,能够有效控制应用包大小。

  • HSP在运行时按需加载,有助于提升应用性能。

  • 同一个组织内部的多个应用之间,可以使用集成态HSP实现代码和资源的共享。

    约束限制
  • HSP不支持在设备上单独安装/运行,需要与依赖该HSP的HAP一起安装/运行。HSP的版本号必须与HAP版本号一致

  • HSP不支持在配置文件中声明UIAbility组件与ExtensionAbility组件。

  • HSP可以依赖其他HAR或HSP,但不支持循环依赖,也不支持依赖传递。

  • 集成态HSP只支持==Stage模型==。

  • 集成态HSP需要API12及以上版本,使用标准化的OHMUrl格式。

    创建

    通过DevEco Studio创建一个HSP模块,详见创建HSP模块,我们以创建一个名为library的HSP模块为例。基本的工程目录结构如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    MyApplication
    ├── library
    │ ├── src
    │ │ └── main
    │ │ ├── ets
    │ │ │ └── pages
    │ │ │ └── index.ets
    │ │ ├── resources
    │ │ └── module.json5
    │ ├── oh-package.json5
    │ ├── index.ets
    │ └── build-profile.json5 //模块级
    └── build-profile.json5 //工程级
    开发

    介绍如何导出HSP的ArkUI组件、接口、资源,供应用内的其他HAP/HSP引用。

    导出ArkUI组件

    ArkUI组件可以通过export导出,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // library/src/main/ets/components/MyTitleBar.ets
    @Component
    export struct MyTitleBar {
    build() {
    Row() {
    Text($r('app.string.library_title'))
    .id('library')
    .fontFamily('HarmonyHeiTi')
    .fontWeight(FontWeight.Bold)
    .fontSize(32)
    .fontColor($r('app.color.text_color'))
    }
    .width('100%')
    }
    }

    对外暴露的接口,需要在入口文件index.ets中声明:

    1
    2
    // library/index.ets
    export { MyTitleBar } from './src/main/ets/components/MyTitleBar';
    导出ts类和方法

    通过export导出ts类和方法,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // library/src/main/ets/utils/test.ts
    export class Log {
    static info(msg: string): void {
    console.info(msg);
    }
    }

    export function add(a: number, b: number): number {
    return a + b;
    }

    export function minus(a: number, b: number): number {
    return a - b;
    }

    对外暴露的接口,需要在入口文件index.ets中声明:

    1
    2
    // library/index.ets
    export { Log, add, minus } from './src/main/ets/utils/test';
    导出native方法

    在HSP中也可以包含C++编写的so。对于so中的native方法,HSP通过间接的方式导出,以导出liblibrary.so的乘法接口multi为例:

    1
    2
    3
    4
    5
    6
    7
    // library/src/main/ets/utils/nativeTest.ts
    import native from 'liblibrary.so';

    export function nativeMulti(a: number, b: number): number {
    let result: number = native.multi(a, b);
    return result;
    }

    对外暴露的接口,需要在入口文件index.ets中声明:

    1
    2
    // library/index.ets
    export { nativeMulti } from './src/main/ets/utils/nativeTest';
    通过$r访问HSP中的资源

    在组件中,经常需要使用字符串、图片等资源。HSP中的组件需要使用资源时,一般将其所用资源放在HSP包内,而非放在HSP的使用方处,以符合高内聚低耦合的原则。
    在工程中,常通过$r/$rawfile的形式引用应用资源。可以用$r/$rawfile访问本模块resources目录下的资源,如访问resources目录下定义的图片src/main/resources/base/media/example.png时,可以用$r("app.media.example")。有关$r/$rawfile的详细使用方式,请参阅文档资源分类与访问中“资源访问-应用资源”小节。
    不推荐使用相对路径的方式,容易引用错误路径。例如:
    当要引用上述同一图片资源时,在HSP模块中使用Image("../../resources/base/media/example.png"),实际上该Image组件访问的是HSP调用方(如entry)下的资源entry/src/main/resources/base/media/example.png

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // library/src/main/ets/pages/Index.ets
    // 正确用例
    Image($r('app.media.example'))
    .id('example')
    .borderRadius('48px')
    // 错误用例
    Image("../../resources/base/media/example.png")
    .id('example')
    .borderRadius('48px')
    导出HSP中的资源

    跨包访问HSP内资源时,推荐实现一个资源管理类,以封装对外导出的资源。采用这种方式,具有如下优点:

  • HSP开发者可以控制自己需要导出的资源,不需要对外暴露的资源可以不用导出。

  • 使用方无须感知HSP内部的资源名称。当HSP内部的资源名称发生变化时,也不需要使用方跟着修改。
    其具体实现如下:
    将需要对外提供的资源封装为一个资源管理类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // library/src/main/ets/ResManager.ets
    export class ResManager{
    static getPic(): Resource{
    return $r('app.media.pic');
    }
    static getDesc(): Resource{
    return $r('app.string.shared_desc');
    }
    }

    对外暴露的接口,需要在入口文件index.ets中声明:

    1
    2
    // library/index.ets
    export { ResManager } from './src/main/ets/ResManager';
    使用

    介绍如何引用HSP中的接口,以及如何通过页面路由实现HSP的pages页面跳转与返回。

    引用HSP中的接口

    要使用HSP中的接口,首先需要在使用方的oh-package.json5中配置对它的依赖,详见引用动态共享包
    依赖配置成功后,就可以像使用HAR一样调用HSP的对外接口了。例如,上面的library已经导出了下面这些接口:

    1
    2
    3
    4
    5
    // library/index.ets
    export { Log, add, minus } from './src/main/ets/utils/test';
    export { MyTitleBar } from './src/main/ets/components/MyTitleBar';
    export { ResManager } from './src/main/ets/ResManager';
    export { nativeMulti } from './src/main/ets/utils/nativeTest';

    在使用方的代码中,可以这样使用:

    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
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    // entry/src/main/ets/pages/index.ets
    import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library';
    import { BusinessError } from '@ohos.base';
    import Logger from '../logger/Logger';
    import router from '@ohos.router';

    const TAG = 'Index';

    @Entry
    @Component
    struct Index {
    @State message: string = '';

    build() {
    Column() {
    List() {
    ListItem() {
    MyTitleBar()
    }
    .margin({ left: '35px', top: '32px' })

    ListItem() {
    Text(this.message)
    .fontFamily('HarmonyHeiTi')
    .fontSize(18)
    .textAlign(TextAlign.Start)
    .width('100%')
    .fontWeight(FontWeight.Bold)
    }
    .width('685px')
    .margin({ top: 30, bottom: 10 })

    ListItem() {
    // ResManager返回的Resource对象,可以传给组件直接使用,也可以从中取出资源来使用
    Image(ResManager.getPic())
    .id('image')
    .borderRadius('48px')
    }
    .width('685px')
    .margin({ top: 10, bottom: 10 })
    .padding({ left: 12, right: 12, top: 4, bottom: 4 })

    ListItem() {
    Text($r('app.string.add'))
    .fontSize(18)
    .textAlign(TextAlign.Start)
    .width('100%')
    .fontWeight(500)
    .height('100%')
    }
    .id('add')
    .borderRadius(24)
    .width('685px')
    .height('84px')
    .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
    .margin({ top: 10, bottom: 10 })
    .padding({ left: 12, right: 12, top: 4, bottom: 4 })
    .onClick(() => {
    Log.info('add button click!');
    this.message = 'result: ' + add(1, 2);
    })

    ListItem() {
    Text($r('app.string.get_string_value'))
    .fontSize(18)
    .textAlign(TextAlign.Start)
    .width('100%')
    .fontWeight(500)
    .height('100%')
    }
    .id('getStringValue')
    .borderRadius(24)
    .width('685px')
    .height('84px')
    .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
    .margin({ top: 10, bottom: 10 })
    .padding({ left: 12, right: 12, top: 4, bottom: 4 })
    .onClick(() => {
    // 先通过当前上下文获取hsp模块的上下文,再获取hsp模块的resourceManager,然后再调用resourceManager的接口获取资源
    getContext()
    .createModuleContext('library')
    .resourceManager
    .getStringValue(ResManager.getDesc())
    .then(value => {
    Logger.info(TAG, `getStringValue is ${value}`);
    this.message = 'getStringValue is ' + value;
    })
    .catch((err: BusinessError) => {
    Logger.info(TAG, `getStringValue promise error is ${err}`);
    });
    })

    ListItem() {
    Text($r('app.string.native_multi'))
    .fontSize(18)
    .textAlign(TextAlign.Start)
    .width('100%')
    .fontWeight(500)
    .height('100%')
    }
    .id('nativeMulti')
    .borderRadius(24)
    .width('685px')
    .height('84px')
    .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
    .margin({ top: 10, bottom: 10 })
    .padding({ left: 12, right: 12, top: 4, bottom: 4 })
    .onClick(() => {
    Log.info('nativeMulti button click!');
    this.message = 'result: ' + nativeMulti(3, 4);
    })
    }
    .alignListItem(ListItemAlign.Center)
    }
    .width('100%')
    .backgroundColor($r('app.color.page_background'))
    .height('100%')
    }
    }
    页面路由跳转

    若开发者想在entry模块中,添加一个按钮跳转至library模块中的menu页面(路径为:library/src/main/ets/pages/menu.ets),那么可以在使用方的代码(entry模块下的Index.ets,路径为:entry/src/main/ets/pages/Index.ets)里这样使用:

    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
    import { Log, add, MyTitleBar, ResManager, nativeMulti } from 'library';
    import { BusinessError } from '@ohos.base';
    import Logger from '../logger/Logger';
    import router from '@ohos.router';

    const TAG = 'Index';

    @Entry
    @Component
    struct Index {
    @State message: string = '';

    build() {
    Column() {
    List() {
    ListItem() {
    Text($r('app.string.click_to_menu'))
    .fontSize(18)
    .textAlign(TextAlign.Start)
    .width('100%')
    .fontWeight(500)
    .height('100%')
    }
    .id('clickToMenu')
    .borderRadius(24)
    .width('685px')
    .height('84px')
    .backgroundColor($r('sys.color.ohos_id_color_foreground_contrary'))
    .margin({ top: 10, bottom: 10 })
    .padding({ left: 12, right: 12, top: 4, bottom: 4 })
    .onClick(() => {
    router.pushUrl({
    url: '@bundle:com.samples.hspsample/library/ets/pages/Menu'
    }).then(() => {
    console.log('push page success');
    Logger.info(TAG, 'push page success');
    }).catch((err: BusinessError) => {
    Logger.error(TAG, `pushUrl failed, code is ${err.code}, message is ${err.message}`);
    })
    })
    }
    .alignListItem(ListItemAlign.Center)
    }
    .width('100%')
    .backgroundColor($r('app.color.page_background'))
    .height('100%')
    }
    }

    其中router.pushUrl方法的入参中url的内容为:

    1
    '@bundle:com.samples.hspsample/library/ets/pages/Menu'

    url内容的模板为:

    1
    '@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
    页面路由返回

    如果当前处于HSP中的页面,需要返回之前的页面时,可以使用router.back方法,但是返回的页面必须是当前页面跳转路径上的页面。

    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
    import router from '@ohos.router';

    @Entry
    @Component
    struct Index3 { // 路径为:`library/src/main/ets/pages/Back.ets
    @State message: string = 'HSP back page';

    build() {
    Row() {
    Column() {
    Text(this.message)
    .fontFamily('HarmonyHeiTi')
    .fontWeight(FontWeight.Bold)
    .fontSize(32)
    .fontColor($r('app.color.text_color'))
    .margin({ top: '32px' })
    .width('624px')

    Button($r('app.string.back_to_HAP'))
    .id('backToHAP')
    .fontFamily('HarmonyHeiTi')
    .height(48)
    .width('624px')
    .margin({ top: 550 })
    .type(ButtonType.Capsule)
    .borderRadius($r('sys.float.ohos_id_corner_radius_button'))
    .backgroundColor($r('app.color.button_background'))
    .fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
    .fontSize($r('sys.float.ohos_id_text_size_button1'))
    // 绑定点击事件
    .onClick(() => {
    router.back({ // 返回HAP的页面
    url: 'pages/Index' // 路径为:`entry/src/main/ets/pages/Index.ets`
    })
    })

    Button($r('app.string.back_to_HSP'))
    .id('backToHSP')
    .fontFamily('HarmonyHeiTi')
    .height(48)
    .width('624px')
    .margin({ top: '4%' , bottom: '6%' })
    .type(ButtonType.Capsule)
    .borderRadius($r('sys.float.ohos_id_corner_radius_button'))
    .backgroundColor($r('app.color.button_background'))
    .fontColor($r('sys.color.ohos_id_color_foreground_contrary'))
    .fontSize($r('sys.float.ohos_id_text_size_button1'))
    // 绑定点击事件
    .onClick(() => {
    router.back({ // 返回HSP的页面
    url: '@bundle:com.samples.hspsample/library/ets/pages/Menu' //路径为:`library/src/main/ets/pages/Menu.ets
    })
    })
    }
    .width('100%')
    }
    .backgroundColor($r('app.color.page_background'))
    .height('100%')
    }
    }

    页面返回router.back方法的入参中url说明:

  • 如果从HSP页面返回HAP页面,url的内容为:

    1
    'pages/Index'

    url内容的模板为:

    1
    '页面所在的文件名(不加.ets后缀)'
  • 如果从HSP1的页面跳到HSP2的页面后,需要返回到HSP1的页面,url的内容为:

    1
    '@bundle:com.samples.hspsample/library/ets/pages/Menu'

    url内容的模板为:

    1
    '@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
集成态HSP
配置HSP模块为集成态HSP

修改模块级构建配置文件build-profile.json5,设置配置项integratedHsptrue,指定构建的HSP模块为集成态HSP。

1
2
3
4
5
6
7
8
{
"apiType": "stageMode",
"buildOption": {
"arkOptions": {
"integratedHsp": true
}
}
}
配置工程使用标准化的OHMUrl格式

集成态HSP需要使用标准化的OHMUrl格式,修改工程级构建配置文件build-profile.json5,设置配置项useNormalizedOHMUrltrue,指定工程使用标准化的OHMUrl格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"app": {
"products": {
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.0(12)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"useNormalizedOHMUrl": true
}
}
}
}
}

应用程序包开发与使用-动态import

动态import支持条件延迟加载,支持部分反射功能,可以提升页面的加载速度;动态import支持加载HSP模块/HAR模块/OHPM包/Native库等,并且HAR模块间只有变量动态import时还可以进行模块解耦。

技术适用场景介绍

应用开发的有些场景中,如果希望根据条件导入模块或者按需导入模块,可以使用动态导入代替静态导入。下面是可能会需要动态导入的场景:

  • 当静态导入的模块很明显的降低了代码的加载速度且被使用的可能性很低,或者并不需要马上使用它。
  • 当静态导入的模块很明显的占用了大量的系统内存且被使用的可能性很低。
  • 当被导入的模块,在加载时并不存在,需要异步获取。
  • 当被导入的模块说明符,需要动态构建。(静态导入只能使用静态说明符)
  • 当被导入的模块有副作用(这里的副作用,可以理解为模块中会直接运行的代码),这些副作用只有在触发了某些条件才被需要时。
业务扩展场景介绍

动态import在业务上除了能实现条件延迟加载,还可以实现部分反射功能。实例如下,HAP动态import HAR包harlibrary,并调用静态成员函数staticAdd()、成员函数instanceAdd(),以及全局方法addHarlibrary()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// harlibrary's src/main/ets/utils/Calc.ets
export class Calc {
public static staticAdd(a:number, b:number):number {
let c = a + b;
console.log('DynamicImport I am harlibrary in staticAdd, %d + %d = %d', a, b, c);
return c;
}

public instanceAdd(a:number, b:number):number {
let c = a + b;
console.log('DynamicImport I am harlibrary in instanceAdd, %d + %d = %d', a, b, c);
return c;
}
}

export function addHarlibrary(a:number, b:number):number {
let c = a + b;
console.log('DynamicImport I am harlibrary in addHarlibrary, %d + %d = %d', a, b, c);
return c;
}

应用程序包安装卸载与更新

本章节简要介绍应用程序包的安装卸载流程、以及应用程序包的两种更新方式。

应用程序包的安装卸载

开发者可以通过调试命令进行应用的安装和卸载,可参考编译发布与上架部署流程图
图1 应用程序包安装和卸载流程(开发者)
应用程序包安装和卸载流程
开发者将应用上架应用市场后,终端设备用户可以在终端设备上使用应用市场进行应用的安装和卸载。

图2 应用程序包安装和卸载流程(终端设备用户)
应用程序包安装和卸载流程

应用程序包的更新

应用程序包可以通过以下两种方式进行更新:

  • 应用市场内更新:新版本应用通过应用市场上架后,应用市场通知终端用户该应用有新版本,终端用户可以根据通知到应用市场(客户端)进行应用升级。
  • 应用内检测升级:终端用户启动应用时,应用市场检测到该应用有新版本会通知终端用户,可以到应用市场进行应用的下载更新。

应用配置文件(Stage模型)

应用配置文件概述(Stage模型)

每个应用项目的代码目录下必须包含应用配置文件,这些配置文件会向编译工具、操作系统和应用市场提供应用的基本信息。
在基于Stage模型开发的应用项目代码下,都存在一个app.json5配置文件、以及一个或多个module.json5配置文件。

app.json5配置文件主要包含以下内容:

  • 应用的全局配置信息,包含应用的Bundle名称、开发厂商、版本号等基本信息。
  • 特定设备类型的配置信息。

module.json5配置文件主要包含以下内容:

  • Module的基本配置信息,包含Module名称、类型、描述、支持的设备类型等基本信息。
  • 应用组件信息,包含UIAbility组件和ExtensionAbility组件的描述信息。
  • 应用运行过程中所需的权限信息。

app.json5配置文件

配置文件示例

先通过一个示例,整体认识一下app.json5配置文件。

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
{
"app": {
"bundleName": "com.application.myapplication",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name",
"description": "$string:description_application",
"minAPIVersion": 9,
"targetAPIVersion": 9,
"apiReleaseType": "Release",
"debug": false,
"car": {
"minAPIVersion": 8
},
"targetBundleName": "com.application.test",
"targetPriority": 50,
"appEnvironments": [
{
"name":"name1",
"value": "value1"
}
],
"maxChildProcess": 5
},
}
配置文件标签

app.json5配置文件包含以下标签。

表1 app.json5配置文件标签说明

属性名称 含义 数据类型 是否可缺省
bundleName 标识应用的Bundle名称,用于标识应用的唯一性。命名规则如下 :
- 由字母、数字、下划线和符号“.”组成,且必须以字母开头
- 字符串最小长度为7字节,最大长度128字节。
- 推荐采用反域名形式命名(如“com.example.demo”,建议第一级为域名后缀com,第二级为厂商/个人名,第三级为应用名,也可以多级)。
对于随系统源码编译的应用,建议命名为“com.ohos.demo”形式,其中的ohos标识系统应用。
字符串 该标签==不可缺省==。
bundleType 标识应用的Bundle类型,用于区分应用或者元服务。支持的取值如下:
- app:当前Bundle为应用。
- atomicService:当前Bundle为元服务。
- shared:当前Bundle为共享库应用,预留字段。
- appService:当前Bundle为系统级共享库应用,仅供系统应用使用。
字符串 该标签可缺省,缺省值为app。
debug 标识应用是否可调试。
- true:可以进行断点调试。
- false:不可以进行断点调试。
布尔值 由IDE编译构建时生成。该标签可缺省,缺省值为false。
icon 标识应用的图标,取值为图标资源文件的索引。 字符串 该标签==不可缺省==。
label 标识应用的名称,取值为字符串资源的索引,字符串长度不超过63字节。 字符串 该标签==不可缺省==。
description 标识应用的描述信息。取值为长度不超过255字节的字符串,内容为描述信息的字符串资源索引。 字符串 该标签可缺省,缺省值为空。
vendor 标识对应用开发厂商的描述,取值为长度不超过255字节的字符串。 字符串 该标签可缺省,缺省值为空。
versionCode 标识应用的版本号,取值为小于2^31次方的正整数。此数字仅用于确定某个版本是否比另一个版本更新,数值越大表示版本越高。
开发者可以将该值设置为任何正整数,但是必须确保应用的新版本都使用比旧版本更大的值。
数值 该标签==不可缺省==。
versionName 标识向用户展示的应用版本号。
取值为长度不超过127字节的字符串,仅由数字和点构成,推荐采用“A.B.C.D”四段式的形式。四段式推荐的含义如下所示。
第一段:主版本号/Major,范围099,重大修改的版本,如实现新的大功能或重大变化。
第二段:次版本号/Minor,范围0
99,表示实现较突出的特点,如新功能添加或大问题修复。
第三段:特性版本号/Feature,范围099,标识规划的新版本特性。
第四段:修订版本号/Patch,范围0
999,表示维护版本,如修复bug。
字符串 该标签==不可缺省==。
minCompatibleVersionCode 标识应用能够兼容的最低历史版本号,用于应用跨设备兼容性判断。取值范围为0~2147483647。 数值 该标签可缺省,缺省值等于versionCode标签值。
minAPIVersion 标识应用运行需要的SDK的API最小版本。取值范围为0~2147483647。 数值 应用编译构建时由build-profile.json5中的compatibleSdkVersion自动生成。
targetAPIVersion 标识应用运行需要的API目标版本。取值范围为0~2147483647。 数值 应用编译构建时由build-profile.json5中的compileSdkVersion自动生成。
apiReleaseType 标识应用运行需要的API目标版本的类型,采用字符串类型表示。取值为“CanaryN”、“BetaN”或者“Release”,其中,N代表大于零的整数。
- Canary:受限发布的版本。
- Beta:公开发布的Beta版本。
- Release:公开发布的正式版本。
字符串 应用编译构建时根据当前使用的SDK的Stage自动生成。即便手动配置了取值,编译构建时也会被覆盖。
accessible 标识应用是否能访问应用的安装目录,仅针对Stage模型的系统应用和预置应用生效。 布尔值 该标签可缺省,缺省值为false。
multiProjects 标识当前工程是否支持多个工程的联合开发。
- true:当前工程支持多个工程的联合开发。多工程开发可参考多工程构建
- false:当前工程不支持多个工程的联合开发。
布尔值 该标签可缺省,缺省值为false。
asanEnabled 标识应用程序是否开启asan检测,用于辅助定位buffer越界造成的crash问题。
- true:当前工程开启asan检测。
- false:当前工程不开启asan检测。
布尔值 该标签可缺省,缺省值为false。
tablet 标识对tablet设备做的特殊配置,可以配置的属性字段有上文提到的:minAPIVersion。
如果使用该属性对tablet设备做了特殊配置,则应用在tablet设备中会采用此处配置的属性值,并忽略在app.json5公共区域配置的属性值。
对象 该标签可缺省,缺省时tablet设备使用app.json5公共区域配置的属性值。
tv 标识对tv设备做的特殊配置,可以配置的属性字段有上文提到的:minAPIVersion。
如果使用该属性对tv设备做了特殊配置,则应用在tv设备中会采用此处配置的属性值,并忽略在app.json5公共区域配置的属性值。
对象 该标签可缺省,缺省时tv设备使用app.json5公共区域配置的属性值。
wearable 标识对wearable设备做的特殊配置,可以配置的属性字段有上文提到的:minAPIVersion。
如果使用该属性对wearable设备做了特殊配置,则应用在wearable设备中会采用此处配置的属性值,并忽略在app.json5公共区域配置的属性值。
对象 该标签可缺省,缺省时wearable设备使用app.json5公共区域配置的属性值。
car 标识对car设备做的特殊配置,可以配置的属性字段有上文提到的:minAPIVersion。
如果使用该属性对car设备做了特殊配置,则应用在car设备中会采用此处配置的属性值,并忽略在app.json5公共区域配置的属性值。
对象 该标签可缺省,缺省时car设备使用app.json5公共区域配置的属性值。
default 标识对default设备做的特殊配置,可以配置的属性字段有上文提到的:minAPIVersion。
如果使用该属性对default设备做了特殊配置,则应用在default设备中会采用此处配置的属性值,并忽略在app.json5公共区域配置的属性值。
对象 该标签可缺省,缺省时default设备使用app.json5公共区域配置的属性值。
targetBundleName 标识当前包所指定的目标应用, 标签值的取值规则和范围与bundleName标签一致。配置该字段的应用为具有overlay特征的应用。 字符串 该标签可缺省,缺省值为空。
targetPriority 标识当前应用的优先级,取值范围为1~100。配置targetBundleName字段之后,才支持配置该字段。 数值 该标签可缺省, 缺省值为1。
generateBuildHash 标识当前应用的所有HAP和HSP是否由打包工具生成哈希值。
该字段配置为true时,该应用下的所有HAP和HSP都会由打包工具生成对应的哈希值。系统OTA升级时,若应用的versionCode保持不变,可根据哈希值判断应用是否需要升级。
说明:
该字段仅对预置应用生效
布尔值 该标签可缺省, 缺省值为false。
GWPAsanEnabled 标识应用程序是否开启GWP-asan堆内存检测工具,用于对内存越界、内存释放后使用等内存破坏问题进行分析。
- true:当前工程开启GWP-asan检测。
- false:当前工程不开启GWP-asan检测。
布尔值 该标签可缺省, 缺省值为false。
appEnvironments 标识当前模块配置的应用环境变量。 对象数组 该标签可缺省,缺省值为空。
maxChildProcess 标识当前应用自身可创建的子进程的最大个数,取值范围为0到512,0表示不限制,当应用有多个模块时,以entry模块的配置为准。 数值 该标签可缺省,缺省时使用系统配置的默认值。
appEnvironments标签

此标签标识应用配置的环境变量。

表1 appEnvironments标签说明

属性名称 含义 数据类型 是否可缺省
name 标识环境变量的变量名称。取值为长度不超过4096字节的字符串。 字符串 该标签可缺省,缺省值为空。
value 标识环境变量的值。取值为长度不超过4096字节的字符串。 字符串 该标签可缺省,缺省值为空。

appEnvironments标签示例:

1
2
3
4
5
6
7
8
9
10
{
"app": {
"appEnvironments": [
{
"name":"name1",
"value": "value1"
}
]
}
}

module.json5配置文件

应用配置文件(FA模型)

应用配置文件概述(FA模型)

app对象内部结构

deviceConfig内部结构

module对象内部结构

资源分类与访问

学习ArkTS语言

初识ArkTS语言

ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。
从API version 10开始,ArkTS进一步通过规范强化静态检查和分析,对比标准TS的差异可以参考从TypeScript到ArkTS的适配规则

  • 强制使用静态类型:静态类型是ArkTS最重要的特性之一。如果使用静态类型,那么程序中变量的类型就是确定的。同时,由于所有类型在程序实际运行前都是已知的,编译器可以验证代码的正确性,从而减少运行时的类型检查,有助于性能提升。
  • ==禁止在运行时改变对象布局==:为实现最大性能,ArkTS要求在程序执行期间不能更改对象布局。
  • 限制运算符语义:为获得更好的性能并鼓励开发者编写更清晰的代码,ArkTS限制了一些运算符的语义。比如,一元加法运算符只能作用于数字,不能用于其他类型的变量。
  • 不支持Structural typing:对Structural typing的支持需要在语言、编译器和运行时进行大量的考虑和仔细的实现,当前ArkTS不支持该特性。根据实际场景的需求和反馈,我们后续会重新考虑。

当前,在UI开发框架中,ArkTS主要扩展了如下能力:

  • 基本语法:ArkTS定义了声明式UI描述、自定义组件和动态扩展UI元素的能力,再配合ArkUI开发框架中的系统组件及其相关的事件方法、属性方法等共同构成了UI开发的主体。
  • 状态管理:ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。另外,从数据的传递形式来看,可分为只读的单向传递和可变更的双向传递。开发者可以灵活地利用这些能力来实现数据和UI的联动。
  • 渲染控制:ArkTS提供了渲染控制的能力。条件渲染可根据应用的不同状态,渲染对应状态下的UI内容。循环渲染可从数据源中迭代获取数据,并在每次迭代过程中创建相应的组件。数据懒加载从数据源中按需迭代数据,并在每次迭代过程中创建相应的组件。

ArkTS兼容TS/JavaScript(简称JS)生态,开发者可以使用TS/JS进行开发或复用已有代码。HarmonyOS系统对TS/JS支持的详细情况参见兼容TS/JS的约束
未来,ArkTS会结合应用开发/运行的需求持续演进,逐步提供并行和并发能力增强、系统类型增强、分布式开发范式等更多特性。

ArkTS语言介绍

通过取消动态类型特性,ArkTS代码能更有效地被运行前编译和优化,从而实现更快的应用启动和更低的功耗。

方舟字节码基本原理

方舟字节码中,除寄存器和累加器之外,还存在全局变量模块(module)命名空间和模块变量词法环境和词法变量补丁变量4种值存储方式。指令可以使用这4种储值位置中的值作为入参。

方舟字节码文件格式

ArkWeb

https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101717497640610394

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
// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct WebComponent {
controller: webview.WebviewController = new webview.WebviewController();

build() {
Column() {
Button('loadUrl')
.onClick(() => {
try {
// 点击按钮时,通过loadUrl,跳转到www.example1.com【就是加载了新的url页面】
this.controller.loadUrl('www.example1.com');
// 点击按钮时,通过loadData,加载HTML格式的文本数据
//this.controller.loadData("<html><body bgcolor=\"white\">Source:<pre>source</pre></body></html>","text/html","UTF-8");
} catch (error) {
let e: BusinessError = error as BusinessError;
console.error(`ErrorCode: ${e.code}, Message: ${e.message}`);
}
})
// 组件创建时,加载www.example.com
Web({ src: 'www.example.com', controller: this.controller})
//Web({
// src:this.articleDetail.webUrl, //修改Web组件的src属性
// //src:$rawfile('sourse_learning/index.html'), //加载本地Web资源
// controller:this.webviewController
//})
}
}
}

其他对WebView的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Web({src: this.VrImage, controller: this.controller})
.blockNetwork(this.block)//拦截网络请求,在请求被发起之前进行处理,允许开发者自定义请求的执行逻辑。【1. **避免请求**: 可以使用这个功能来完全阻止某些特定的网络请求,比如为了解决特定的安全问题或者在开发和测试阶段禁用某些外部 API 调用。2. **修改请求**: 允许开发者修改即将发出的网络请求,例如为请求添加自定义头部信息或修改请求参数。3. **条件控制**: 可以根据某些条件(例如用户的网络状态、设备类型等)决定是否允许特定的请求执行。】
.javaScriptAccess(true)
.darkMode(WebDarkMode.Auto)//深色模式设置  WebDarkMode.Auto表示开启深色模式,并且深色模式跟随系统
.backgroundColor(0xFFFFFF)//白色
.zoomAccess(true)//设置可以支持缩放
.databaseAccess(true)//设置是否开启数据库存储A
.geolocationAccess(true)//启用地理位置
// .textZoomRatio(100)//设置页面的文本缩放百分比,默认为100%。
.cacheMode(CacheMode.Default)//默认
.fileAccess(true)//设置是否开启应用中文件系统的访问,默认启用。
.mixedMode(MixedMode.All)//设置是否允许加载超文本传输协议(HTTP)和超文本传输安全协议(HTTPS)混合内容,默认不允许加载HTTP和HTTPS混合内容。
.domStorageAccess(true)
.allowWindowOpenMethod(true)//网页是否可以通过JavaScript自动打开窗口。

架构

架构

  • 分层架构设计:将应用划分为产品定制层、基础特性层和共功能力层,可以降低层间的依赖性,提高可维护性。进一步明确每层的职责和层间的交互机制,为开发者呈现一个清晰且结构化的开发框架。
  • 模块化设计:将应用分解为多个功能模块,每个模块负责执行特定的功能。提高代码的可理解性和可复用性,使应用的扩展和维护变得更为简便,也降低了系统各部分之间的耦合度。
  • 应用导航设计:将应用划分为多功能模块 后,通过Navigation导航设计,完成业务模块之间的解耦,实现不同业务模块之间的页面跳转交互。
  • 其他文章:
  1. 探索鸿蒙:HAP/HAR/HSP,你用对了吗在软件开发的各个领域,都少不了共享库的存在, Java 中的 JAR ,An - 掘金
  2. HMOSWorld: 基于分层架构和模块化设计的最佳实践,结合HarmonyOS”一次开发,多端部署”的能力,实现一款承载HarmonyOS最新技术特性,向HarmonyOS开发者赋能的示例应用。