第3章

万事俱备: 基础知识




3.1开发基础知识
本章简单讲解各模块的应用,如果已经掌握了这些开发基础知识,则可以直接进入第4章的实战演练。
3.1.1程序
用户应用程序: 用户应用程序泛指运行在设备的操作系统之上,为用户提供特定服务的程序,简称应用。
在HarmonyOS上运行的应用有两种形态: 传统方式的需要安装的应用和提供特定功能免安装的应用(原子化服务)。
目前大部分应用程序包是App包,而HarmonyOS的用户应用程序包以App Pack(Application Package)的形式发布,它由一个或多个HAP(HarmonyOS Ability Package)及描述每个HAP属性的pack.info组成。HAP是Ability的部署包,HarmonyOS应用代码围绕Ability组件展开。
一个HAP是由代码、资源、第三方库及应用配置文件组成的模块包,可分为Entry和Feature两种模块类型,如图31所示。


图31App逻辑视图


Entry: 应用的主模块。在一个App中,对于同一设备类型必须有且只有一个Entry类型的HAP,可独立安装运行。
Feature: 应用的动态特性模块。一个App可以包含一个或多个Feature类型的HAP,也可以不包含。只有包含Ability的HAP才能独立运行。
3.1.2配置文件
应用的每个HAP的根目录下都存在一个config.json配置文件,文件内容主要涵盖以下3方面: 
(1) 应用的全局配置信息,包含应用的包名、生产厂商、版本号等基本信息。
(2) 应用在具体设备上的配置信息,包含应用的备份恢复、网络安全等能力。
(3) HAP包的配置信息,包含每个Ability必须定义的基本属性(如包名、类名、类型及Ability提供的能力),以及应用访问系统或其他应用受保护部分所需的权限等。
其中配置文件config.json采用JSON文件格式,其中包含了一系列配置项,每个配置项由属性和值两部分构成。
(1) 属性: 属性出现的顺序不分先后,并且每个属性最多只允许出现一次。
(2) 值: 每个属性的值为JSON的基本数据类型(数值、字符串、布尔值、数组、对象或者null类型)。
3.1.3资源文件
1. resources目录
应用的资源文件(字符串、图片、声频等)统一存放于resources目录下,便于开发者使用和维护。resources目录包括两大类目录,一类为base目录与限定词目录; 另一类为rawfile目录,目录如下: 


resources

|---base  //默认存在的目录

|   |---element

|   ||---string.json

|   |---media

|   ||---icon.png

|---en_GB-vertical-car-mdpi//限定词目录示例,需要开发者自行创建   

|   |---element




|   ||---string.json

|   |---media

|   ||---icon.png

|---rawfile   //默认存在的目录

(1) 按组织形式分: base目录与限定词目录按照两级目录形式来组织,目录命名必须符合规范,以便根据设备状态匹配相应目录下的资源文件。其中,一级子目录为base目录和限定词目录。base目录是默认存在的目录。当应用的resources资源目录中没有与设备状态匹配的限定词目录时,会自动引用该目录中的资源文件。限定词目录需要开发者自行创建。目录名称由一个或多个表征应用场景或设备特征的限定词组合而成。二级子目录为资源目录,用于存放字符串、颜色、布尔值等基础元素,以及媒体、动画、布局等资源文件; rawfile目录支持创建多层子目录,目录名称可以自定义,文件夹内可以自由放置各类资源文件。rawfile目录的文件不会根据设备状态去匹配不同的资源。
(2) 按编译方式分: base目录与限定词目录中的资源文件会被编译成二进制文件,并赋予资源文件ID; rawfile目录中的资源文件会被直接打包进应用,不经过编译,也不被赋予资源文件ID。
(3) 按引用方式分: base目录与限定词目录通过指定资源类型(type)和资源名称(name)来引用; rawfile目录通过指定文件路径和文件名来引用。
2. 限定词目录
限定词目录可以由一个或多个表征应用场景或设备特征的限定词组合而成,包括移动国家码和移动网络码、语言、文字、国家或地区、横竖屏、设备类型、颜色模式和屏幕密度等维度,限定词之间通过下画线“_”或者半字线“”连接。开发者在创建限定词目录时,需要掌握限定词目录的命名要求,以及限定词目录与设备状态的匹配规则。
其中限定词目录的命名要求有以下三点。
(1) 限定词的组合顺序: 移动国家码_移动网络码语言_文字_国家或地区横竖屏设备类型深色模式屏幕密度。开发者可以根据应用的使用场景和设备特征,选择其中的一类或几类限定词组成目录名称。
(2) 限定词的连接方式: 语言、文字、国家或地区之间采用下画线“_”连接,移动国家码和移动网络码之间也采用下画线“_”连接,除此之外的其他限定词之间均采用半字线“”连接。例如,zh_Hant_CN、zh_CNcarldpi。
(3) 限定词的取值范围: 每类限定词的取值必须符合限定词类型的条件,否则将无法匹配目录中的资源文件。
限定词目录与设备状态的匹配规则有以下两点: 
(1) 在为设备匹配对应的资源文件时,限定词目录匹配的优先级从高到低依次为移动国家码和移动网络码>区域(可选组合: 语言、语言_文字、语言_国家或地区、语言_文字_国家或地区)>横竖屏>设备类型>颜色模式>屏幕密度。
(2) 如果限定词目录中包含移动国家码和移动网络码、语言、文字、横竖屏、设备类型、颜色模式限定词,则对应限定词的取值必须与当前的设备状态完全一致,这样该目录才能参与设备的资源匹配。例如,限定词目录zh_CNcarldpi不能参与en_US设备的资源匹配。
3. 资源组目录
在base目录与限定词目录下面可以创建资源组目录,包括element、media、animation、layout、graphic、profile,用于存放特定类型的资源文件。
其中,element目录表示元素资源,以下每一类数据都采用相应的JSON文件来表征。media目录表示媒体资源,包括图片、声频、视频等非文本格式的文件。animation目录表示动画资源,采用XML文件格式。layout目录表示布局资源,采用XML文件格式。graphic目录表示可绘制资源,采用XML文件格式。profile目录表示其他类型文件,以原始文件的形式保存。
3.1.4其他
(1) Ability: Ability是应用所具备的能力的抽象,一个应用可以包含一个或多个Ability。Ability分为两种类型: FA(Feature Ability)和PA(Particle Ability)。FA/PA是应用的基本组成单元,能够实现特定的业务功能。FA有UI界面,而PA无UI界面。
(2) 库文件: 库文件是应用依赖的第三方代码(例如so、jar、bin、har等二进制文件),存放在libs目录。
(3) pack.info: 描述应用软件包中每个HAP的属性,由IDE编译生成,应用市场根据该文件进行拆包和HAP的分类存储。
(4) deliverywithinstall: 表示该HAP是否支持随应用安装。true表示支持随应用安装,false表示不支持随应用安装。
(5) name: HAP文件名。
(6) moduletype: 模块类型,即Entry或Feature。
(7) devicetype: 表示支持该HAP运行的设备类型。
(8) HAR: HAR(HarmonyOS Ability Resources)可以提供构建应用所需的所有内容,包括源代码、资源文件和config.json文件。HAR不同于HAP,HAR不能独立安装运行在设备上,只能作为应用模块的依赖项被引用。
3.2Page Ability
Ability是应用所具备能力的抽象,也是应用程序的重要组成部分。一个应用可以具备多种能力(可包含多个Ability),HarmonyOS支持应用以Ability为单位进行部署。每种类型为开发者提供了不同的模板,以便实现不同的业务功能。
(1) FA支持Page Ability: Page模板是FA唯一支持的模板,用于提供与用户交互的能力。一个Page实例可以包含一组相关页面,每个页面用一个AbilitySlice实例表示。
(2) PA支持Service Ability和Data Ability: Service模板用于提供后台运行任务的能力; Data模板用于对外部提供统一的数据访问抽象。
在配置文件(config.json)中注册Ability时,可以通过配置Ability元素中的type属性来指定Ability的模板类型,其中type的取值可以为page、service或data,分别代表Page模板、Service模板、Data模板,伪代码如下: 


{

"module": {

...

"abilities": [

{

...

"type": "page"

...

}

]

...

}

...

}



图32Page和AbilitySlice

的关系

Page模板是FA唯一支持的模板,用于提供与用户交互的能力。一个Page可以由一个或多个AbilitySlice构成,AbilitySlice是指应用的单个页面及其控制逻辑的总和。
当一个Page由多个AbilitySlice共同构成时,这些AbilitySlice页面提供的业务能力应具有高度相关性。例如,新闻浏览功能可以通过一个Page实现,其中包含了两个AbilitySlice: 一个AbilitySlice用于展示新闻列表; 另一个AbilitySlice用于展示新闻详情。Page和AbilitySlice的关系如图32所示。
3.2.1Page的生命周期
对于每个Page都拥有相同的生命周期函数: onStart()、onActive()、onInactive()、onBackground()、onForeground()和onStop()。
(1) onStart(): 当系统首次创建Page实例时,触发该回调。对于一个Page实例,该回调在其生命周期过程中仅触发一次,Page在该逻辑后将进入INACTIVE状态。开发者必须重写该方法,并在此配置默认展示的AbilitySlice。
(2) onActive(): Page会在进入INACTIVE状态后来到前台,然后由系统调用此回调。Page在此之后进入ACTIVE状态,该状态是应用与用户进行交互的状态。Page将保持在此状态,除非某类事件导致Page失去焦点,例如用户单击返回键或导航到其他Page。当此类事件发生时,会触发Page回到INACTIVE状态,系统将调用onInactive()回调函数。此后,Page可能重新回到ACTIVE状态,系统将再次调用onActive()回调函数,因此,开发者通常需要成对实现onActive()和onInactive()函数,并在onActive()函数中获取在onInactive()函数中被释放的资源。
(3) onInactive(): 当Page失去焦点时,系统将调用此回调函数,此后Page进入INACTIVE状态。开发者可以在此回调函数中实现Page失去焦点时应表现的恰当行为。
(4) onBackground(): 如果Page不再对用户可见,则系统将调用此回调函数通知开发者对用户进行相应的资源释放,此后Page进入BACKGROUND状态。开发者应该在此回调函数中释放Page不可见时无用的资源,或在此回调函数中执行较为耗时的状态保存操作。
(5) onForeground(): 处于BACKGROUND状态的Page仍然驻留在内存中,当重新回到前台时(例如用户重新导航到此Page),系统将先调用onForeground()回调函数通知开发者,而后Page的生命周期状态回到INACTIVE状态。开发者应当在此回调函数中重新申请在onBackground()函数中释放的资源,最后Page的生命周期状态进一步回到ACTIVE状态,系统将通过onActive()回调函数通知开发者用户。
(6) onStop(): 系统将要销毁Page时,将会触发此回调函数,通知用户进行系统资源的释放。销毁Page的可能原因包括以下几方面: 用户通过系统管理能力关闭指定Page,例如使用任务管理器关闭Page; 用户行为触发Page的terminateAbility()方法调用,例如使用应用的退出功能; 配置变更导致系统暂时销毁Page并重建; 系统出于资源管理的目的,自动触发对处于BACKGROUND状态的Page进行销毁。
Page的生命周期的验证将在5.6节进行。
3.2.2AbilitySlice的生命周期
AbilitySlice作为Page的组成单元,其生命周期依托于其所属Page的生命周期。AbilitySlice和Page具有相同的生命周期状态和同名的回调函数,当Page的生命周期发生变化时,它的AbilitySlice也会发生相同的生命周期变化。此外,AbilitySlice还具有独立于Page的生命周期变化,这发生在同一Page中的AbilitySlice之间进行导航时,此时Page的生命周期状态不会改变。AbilitySlice的生命周期回调函数与Page的相应回调函数类似,因此不再赘述。
3.2.3Page与AbilitySlice的生命周期关联
当AbilitySlice处于前台且具有焦点时,其生命周期状态随着所属Page的生命周期状态的变化而变化。当一个Page拥有多个AbilitySlice时,例如,MainAbility下有FooAbilitySlice和BarAbilitySlice,当前FooAbilitySlice处于前台并获得焦点,并且即将导航到BarAbilitySlice,在此期间的生命周期状态的变化顺序为
(1) FooAbilitySlice从ACTIVE状态变为INACTIVE状态。
(2) BarAbilitySlice则从INITIAL状态首先变为INACTIVE状态,然后变为ACTIVE状态(假定此前BarAbilitySlice未曾启动)。
(3) FooAbilitySlice从INACTIVE状态变为BACKGROUND状态。
对应两个Slice的生命周期方法的回调顺序为FooAbilitySlice.onInactive()→BarAbilitySlice.onStart()→BarAbilitySlice.onActive()→FooAbilitySlice.onBackground()。
在整个流程中,MainAbility始终处于ACTIVE状态,但是,当Page被系统销毁时,其所有已实例化的AbilitySlice将联动销毁,而不仅销毁处于前台的AbilitySlice。
3.3Service Ability
基于Service模板的Ability主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动,即使用户切换到其他应用,Service仍将在后台继续运行。
Service是单实例的。在一个设备上,相同的Service只会存在一个实例。如果多个Ability共用这个实例,则只有当与Service绑定的所有Ability都退出后,Service才能退出。由于Service是在主线程里执行的,因此,如果在Service里面的操作时间过长,则开发者必须在Service里创建新的线程来处理(详见线程间通信),防止造成主线程阻塞,从而防止应用程序无响应。
与Page类似,Service Ability也拥有生命周期,如图33所示。


图33Service Ability生命周期


根据调用方法的不同,其生命周期有以下两种路径。
(1) 启动Service: 该Service在其他Ability调用startAbility()函数时创建,然后保持运行。其他Ability通过调用stopAbility()函数来停止Service,Service停止后,系统会将其销毁。
(2) 连接Service: 该Service在其他Ability调用connectAbility()函数时创建,客户端可通过调用disconnectAbility()函数断开连接。多个客户端可以绑定到相同的Service,而且当所有绑定全部取消后,系统即会销毁该Service。connectAbility()函数也可以连接通过startAbility()函数创建的Service。
3.4Data Ability
使用Data模板的Ability(以下简称Data)有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。Data对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。
Data的提供方和使用方都通过URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。HarmonyOS的URI仍基于URI通用标准,格式如图34所示。


图34URI


(1) Scheme: 协议方案名,固定为dataability,代表Data Ability所使用的协议类型。
(2) authority: 设备ID。如果为跨设备场景,则为目标设备的ID; 如果为本地设备场景,则不需要填写。
(3) path: 资源的路径信息,代表特定资源的位置信息。
(4) query: 查询参数。
(5) fragment: 可以用于指示要访问的子资源。
3.5JS生命周期
生命周期分为应用生命周期和页面生命周期。
1. 应用生命周期
在app.js文件中可以定义如下应用生命周期函数。
(1) onCreate(): 用于应用创建,当应用创建时调用。
(2) onShow(): 当应用处于前台时触发。
(3) onHide(): 当应用处于后台时触发。
(4) onDestroy(): 当应用退出时触发。
2. 页面生命周期
在页面JS文件中可以定义如下页面生命周期函数。
(1) onInit(): 页面数据初始化完成时触发,只触发一次。
(2) onReady(): 页面创建完成时触发,只触发一次。
(3) onShow(): 页面显示时触发。
(4) onHide(): 页面消失时触发。
(5) onDestroy(): 页面销毁时触发。
(6) onBackPress(): 当用户单击返回按钮时触发。当返回值为true时表示页面自己处理返回逻辑; 当返回值为false时表示使用默认的返回逻辑; 当不返回值时会作为false处理。
(7) onActive(): 页面激活时触发。
(8) onInactive(): 页面暂停时触发。
常见的页面A的生命周期接口的调用顺序如下。
(1) 打开页面A: onInit()→onReady()→onShow()。
(2) 在页面A打开页面B: onHide()。
(3) 从页面B返回页面A: onShow()。
(4) 退出页面A: onBackPress()→onHide()→onDestroy()。
(5) 页面隐藏到后台运行: onInactive()→onHide()。
(6) 页面从后台运行恢复到前台: onShow()→onActive()。
3.6Java UI框架
应用的Ability在屏幕上将显示一个用户界面,该界面用来显示所有可被用户查看和交互的内容。
应用中所有的用户界面元素都由Component和ComponentContainer对象构成。Component是绘制在屏幕上的一个对象,用户能与之交互。ComponentContainer是一个用于容纳其他Component和ComponentContainer对象的容器。
Java UI框架提供了一部分Component和ComponentContainer的具体子类,即创建用户界面(UI)的各类组件,包括一些常用的组件(例如: 文本、按钮、图片、列表等)和常用的布局(例如: DirectionalLayout和DependentLayout)。用户可通过组件进行交互操作,并获得响应。
需要注意的是,所有的UI操作都应该在主线程进行设置。
用户界面元素统称为组件,组件根据一定的层级结构进行组合,从而形成布局。组件在未被添加到布局中时,既无法显示也无法交互,因此一个用户界面至少包含一个布局。在UI框架中,具体的布局类通常以XXLayout命名,完整的用户界面是一个布局,用户界面中的一部分也可以是一个布局。布局中容纳的是Component与ComponentContainer对象。Component与ComponentContainer的关系如图35所示。


图35Component与ComponentContainer的关系


(1) Component: 提供内容显示,是界面中所有组件的基类,开发者可以给Component设置事件处理回调来创建一个可交互的组件。Java UI框架提供了一些常用的界面元素,也可称为组件,组件一般直接继承自Component或它的子类,如Text、Image等。
(2) ComponentContainer: 作为容器容纳Component或ComponentContainer对象,并对它们进行布局。Java UI框架提供了一些标准布局功能的容器,它们继承自ComponentContainer,一般以Layout结尾,如DirectionalLayout、DependentLayout等。
主要模块的开发基础知识就到此讲解完了。第4章将在2.2节Hello World项目的基础上不断地进行修改和完善,最终开发出一个完整的经典游戏App——“数字华容道”。