Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HarmonyOS第一课--基础课程笔记 #79

Open
dwqs opened this issue Nov 14, 2024 · 3 comments
Open

HarmonyOS第一课--基础课程笔记 #79

dwqs opened this issue Nov 14, 2024 · 3 comments

Comments

@dwqs
Copy link
Owner

dwqs commented Nov 14, 2024

前言

最近学习了一下HarmonyOS 第一课的基础课程,此篇以记录一些课程笔记。

系统环境:MacOS 14.6.1(Apple M2 Pro)

核心理念

  • 一次开发,多端部署:应用一次开发就能在多个设备上运行,软件实体能够从单一设备转移到其他设备上,降低针对不同设备和操作系统的重复开发和多套维护的成本
  • 可分可合,自由流转:软件实体在多个设备之间能够协同运行,给消费者提供全新的分布式体验;提供轻量化的服务,最小化资源消耗,一步直达,快速完成消费者特定场景的任务
  • 统一生态,原生智能:为消费者提供智慧场景服务,实现“服务找人”;提供软硬芯协同优化的原生AI能力,全面满足应用高性能诉求

DevEco Studio的使用

DevEco Studio 是HarmonyOS开发的集成开发环境(IDE),可以去官方下载页面去下载最新版本。安装完成之后,可以对IDE进行诊断和汉化。

诊断IDE

诊断主要是识别开发环境是否完备。操作路径:Help > Diagnostic Tools > Diagnose Development Environment。

Diagnostic.png

汉化

IDE默认的语种是英语,但自带了汉化包。操作路径:DevEco Studio > Preferences > Plugins,选择Installed标签,搜索「chinese」,然后点击右侧的「Enable」启用插件,重启IDE即可。

Windows 系统为:File > Settings > Plugins

image.png

本地调试

DevEco Studio 支持预览器、模拟器、真机三种本地调试方式,预览器同时支持手机、平板、可折叠设备等多种设备进行本地调试,操作路径:在工程的右侧点击 Previewer > 点击2所指的图标 > 打开Multi-profile preview,在下面能看到对应的设备列表:

image.png

但需要注意的是,预览器有一部分能力并不支持:

  • 不支持运行Ability生命周期
  • 不支持引用HSP(Harmony Shared Package),引用了HSP的模块不支持预览
  • 不支持通过相对路径及绝对路径的方式访问resources目录下的文件
  • 不支持Richtext、Web、Video、XComponent等组件,也不支持组件拖拽
  • 不支持调用C++库的预览
  • 不支持EventHub等方式进行数据同步

若工程包含有预览器不支持的能力时,建议用模拟器或真机进行调试。

模拟器所支持的能力可见:模拟器和真机调试

ArkTS & ArkUI

目前流行的编程语言TypeScript是在JavaScript基础上通过添加类型定义扩展而来的,而ArkTS在继承TypeScript语法的基础上进行进一步扩展和优化,以提供更高的性能和开发效率。所以,ArkTS的语法和TypeScript的语法非常类似,这对于前端开发者而言就非常友好了。

const t:string = 'aaa'
let b = 2
const c: number[] = [1,2,3]
...
enum ENUM {
  ONE = 1
}
class A {
  name: string = ''
  
  getName(): string {
    return this.name
  }
}
interface B {}

ArkTS要求所有字段在声明时或者构造函数中显式初始化,这和TS中的strictPropertyInitialization模式一样。

ArkTS以声明方式组合和扩展组件来描述应用程序的UI,而ArkUI不仅提供基础的系统布局组件和应用组件,例如TextImageRowColumn等,同时提供了与组件对应的属性、事件和子组件配置方法。看一个简单示例:

...
@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
	
  build() {
  	Column(){
        // 以下都是子组件

        // 文本组件
        Text(this.message)
		  .fontSize(30)
		  .fontWeight(FontWeight.Bold)
        // 图片组件
        Image(src).width(100)
        // 按钮组件
        Button() {
          Text('这是按钮') // 子组件
        }
        .type(ButtonType.Capsule)
        .margin({ top: 40 })
        .onClick(() => { console.log('按钮事件')  })
	  }
	  .height('100%')
	  .width('100%')
  }
}

从上述示例可以看出,对组件属性的配置和CSS的语法是非常像的:

  • @entry@Entry装饰的自定义组件将作为UI页面的入口组件,即页面的根节点。一个页面有且仅能有一个@Entry装饰的组件,这类组件也叫页面组件
  • @component@Component装饰器仅能装饰struct关键字声明的数据结构。struct@Component装饰后具备组件化的能力,需要实现build方法描述UI。一个struct只能被一个@Component装饰,被@Component装饰的组件也叫自定义组件
  • build()函数build()函数用于定义自定义组件的声明式UI描述(作用类似于Vue/React等前端框架的render函数),这是组件必须要声明的函数

组件的生命周期

Vue/React等前端框架的组件一样,ArkUI组件也有自身的生命周期。被@Entry装饰的页面组件生命周期如下图所示:

life-circle

页面组件具备以下生命周期接口:

  • onPageShow:页面每次显示时触发一次,包括路由过程、应用进入前台等场景
  • onPageHide:页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景
  • onBackPress:当用户点击返回按钮时触发

自定义组件具备以下生命周期接口:

  • aboutToAppear:组件即将出现时回调该接口,具体时机为在创建自定义组件的新实例后,在执行其build()函数之前执行。
  • onDidBuild:组件build()函数执行完成之后回调该接口。
  • aboutToDisappear:在自定义组件被销毁之前执行。组件的销毁是从组件树上直接摘下子树,对于嵌套组件而言,会先调用父组件的aboutToDisappear,再调用子组件的aboutToDisappear

ArkUI组件的生命周期接口也支持自定义,具体示例可见声明式UI-页面和自定义组件-自定义组件监听页面生命周期

ForEach循环

循环渲染一组数据是业务开发中常见的场景,前端框架都提供了相关的指令或语法,ForEach是ArkTS提供的基于数组类型数据来进行循环渲染的接口。看一个简单示例:

@Entry
@Component
struct Index{
  arr: string[] = ['one', 'two', 'three']
	
  build(){
    Column(){
      ForEach(this.arr, (item: string) => {
        Text(item).margin({ bottom: 20 })
      })
    }
  }
}

这样就可以把一组数据渲染到界面上:

image.png

ForEach需要与容器组件配合使用,且接口返回的组件应当是允许包含在ForEach父容器组件中的子组件。例如,ListItem组件要求ForEach的父容器组件必须为List组件。它的语法如下:

ForEach(
  Array<Object>, // 数据源
  itemGenerator: (item: Object, index: number) => void, // 组件生成函数
  keyGenerator?: (item: Object, index: number) => string // 键值生成函数
)

前两个参数是必须的,第三个keyGenerator参数是可选的,这个是用于给组件生成键值的,会和生成的组件绑定。在React/Vue等前端框架中,进行循环渲染时都会建议给组件设定key值,以便于复用组件,提升渲染性能,ArkTS也是如此,但又存在不同。举个例子:

// 数据源
arr = ['one', 'two', 'three', 'two']

// vue
<div v-for="item in arr" :key="item" style="margin-top: 20px">
  <div>{{ item }}</div>
</div>

// ArkUI
ForEach(this.arr, (item: string) => {
  Text(item).margin({ bottom: 20 })
}, (item: string) => item)

同样是以arr作为数据源,以arr的数组项作为组件的key值,二者渲染的效果却不同:

difference.png

从上图可以看出,Vue的渲染结果是符合预期的,ArkTS渲染的列表少了数据源的最后一项。这是因为ForEach遍历数据源时将item作为键值,遍历到索引为1的two时,生成键值为two的组件并进行标记;当遍历到索引为3的two时,当前项的键值也为two,此时不再创建新的组件。倘若组件生成的键值是相同的,那么ArkUI框架的行为也是未定义的,就会出现非预期渲染。而对于Vue等前端框架,即使键值相同,依然会创建组件,只是会出现一个「键值重复」的警告。

ForEach的第三个参数是可选的,若未提供则会使用默认的键值生成函数:

(item: Object, index: number) => { return index + '__' + JSON.stringify(item); }

ArkUI框架对于ForEach的键值生成有一套特定的判断规则,这主要与itemGenerator函数的第二个参数index以及keyGenerator函数的第二个参数index有关,具体的键值生成规则判断逻辑如下图所示:

keyGenerator.png

build()函数

上文说过,build()函数的作用类似于Vue/React等前端框架的render函数,负责组件的渲染。无论是页面组件还是自定义组件,都必须显示声明该函数。所有声明在build()函数内的语句,统称为UI描述。UI描述需要遵循以下规则:

  • @Entry装饰的自定义组件,其build()函数下的根节点唯一且必要,且必须为容器组件,其中ForEach禁止作为根节点
  • @Component装饰的自定义组件,其build()函数下的根节点唯一且必要,可以为非容器组件,其中ForEach禁止作为根节点
  • 不允许在该函数体内声明本地变量创建本地作用域使用console语句使用switch语句使用表达式以及直接改变状态变量
build(){
  let a:number = 1 // 编译错误
  console.log('aaa') // 编译错误
  {
    //创建本地作用域,编译错误
  }
  	
  // 不允许,用if语句代替
  switch(exp) {}
  // 不允许使用表达式,用if语句代替
  (this.aVar > 10) ? Text('...') : Image('...')

  // 不允许改变状态变量
  this.a = 1
}
  • 不允许调用没有用@Builder装饰的方法,但允许系统组件的参数是TS方法的返回值。
@Component
struct ParentComponent {
  doSomeCalculations() {
  }

  calcTextValue(): string {
    return 'Hello World';
  }

  @Builder
  doSomeRender() {
    Text(`Hello World`)
  }

  build() {
    Column() {
      // 反例:不能调用没有用@Builder装饰的方法
      this.doSomeCalculations();
      // 正例:可以调用
      this.doSomeRender();
      // 正例:参数可以为调用TS方法的返回值
      Text(this.calcTextValue())
    }
  }
}

UIAbility组件

UIAbility组件是一种包含UI的应用组件,是系统调度的基本单元,主要用于和用户交互,其主要特点有两个:

  • 原生支持应用组件级的跨端迁移和多端协同
  • 支持多设备和多窗口形态

一个应用可以包含一个或多个UIAbility组件,而每个UIAbility组件实例都会在最近任务列表中显示一个对应的任务。对于开发者而言,可以根据具体场景选择单个还是多个UIAbility,划分建议如下:

  • 如果开发者希望在任务视图中看到一个任务,建议使用“一个UIAbility+多个页面”的方式,可以避免不必要的资源加载
  • 如果开发者希望在任务视图中看到多个任务,或者需要同时开启多个窗口,建议使用多个UIAbility实现不同的功能

生命周期

UIAbility的生命周期包括CreateForegroundBackgroundDestroy四个状态:

  • Create状态:UIAbility实例创建完成时触发,系统会调用onCreate()回调,可以在该回调中进行页面初始化操作,例如变量定义资源加载等,用于后续的UI展示
  • Foreground状态:UIAbility切换至前台时触发onForeground回调,可以在该回调中申请系统需要的资源,或者重新申请在onBackground()中释放的资源
  • Background状态:UIAbility切换至后台时触发onBackground回调,可以在该回调中释放UI不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等
  • Destroy状态:UIAbility实例销毁时触发onDestroy回调,可以在该回调中进行系统资源的释放、数据的保存等操作

完整的周期示意图如下:

life-circle.png

启动模式

UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景,系统提供了三种启动模式:

  • singleton(单实例模式):默认的启动模式,每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例

singleton启动模式时,再次调用startAbility()方法启动该UIAbility实例时,只会进入该UIAbility的onNewWant()回调,不会进入其onCreate()onWindowStageCreate()生命周期回调。应用可以在该回调中更新要加载的资源和数据等,用于后续的UI展示。

  • multiton(多实例模式):多实例模式下,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例
  • specified(指定实例模式):指定实例模式下,每次调用startAbility()方法时,会先进入对应的AbilityStageonAcceptWant()生命周期回调中,以获取该UIAbility实例的Key值。然后系统会自动匹配,如果存在与该UIAbility实例匹配的Key,则会启动与之绑定的UIAbility实例,并进入该UIAbility实例的onNewWant()回调函数;否则会创建一个新的UIAbility实例,并进入该UIAbility实例的onCreate()回调函数和onWindowStageCreate()回调函数。

指定实例模式针对一些特殊场景使用,例如文档编辑,重复打开已保存的文档都希望是打开同一个UIAbility实例

应用程序

用户应用程序泛指运行在设备的操作系统之上,为用户提供特定服务的程序,简称“应用”。一个应用所对应的软件包文件,称为“应用程序包”。HarmonyOS为应用的开发提供了多Module设计机制,其特点是:

  • 支持模块化开发:一个应用通常会包含多种功能,将不同的功能特性按模块来划分和管理是一种良好的设计方式。在开发过程中,我们可以将每个功能模块作为一个独立的Module进行开发,Module中可以包含源代码、资源文件、第三方库、配置文件等,每一个Module可以独立编译,实现特定的功能。这种模块化、松耦合的应用管理方式有助于应用的开发、维护与扩展
  • 支持多设备适配: 一个应用往往需要适配多种设备类型,在采用多Module设计的应用中,每个Module都会标注所支持的设备类型。有些Module支持全部类型的设备,有些Module只支持某一种或几种类型的设备(比如平板),那么在应用市场分发应用包时,也能够根据设备类型做精准的筛选和匹配,从而将不同的包合理的组合和部署到对应的设备上

Module类型

Module按照使用场景可以分为两种类型:

  • Ability类型:用于实现应用的功能和特性。每一个Ability类型的Module编译后,会生成一个以.hap为后缀的文件,称为HAP(Harmony Ability Package)包。HAP包可以独立安装和运行,是应用安装的基本单位,一个应用中可以包含一个或多个HAP包,具体包含如下两种类型:
    • entry类型:应用的主模块,包含应用的入口界面、入口图标和主功能特性,编译后生成entry类型的HAP。每一个应用分发到同一类型的设备上的应用程序包,只能包含唯一一个entry类型的HAP
    • feature类型:应用的动态特性模块,编译后生成feature类型的HAP。一个应用中可以包含一个或多个feature类型的HAP,也可以不包含
  • Library类型:用于实现代码和资源的共享。Library类型的Module分为StaticShared两种类型,编译后会生成共享包:
    • Static Library:静态共享库。编译后会生成一个以.har为后缀的文件,即静态共享包HAR(Harmony Archive)
    • Shared Library:动态共享库。编译后会生成一个以.hsp为后缀的文件,即动态共享包HSP(Harmony Shared Package)

对于同一个Library类型的Module,可以被其他的Module多次引用,二者的编译和运行差异如下:

image.png

从上图可以看出,HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝;而HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份。

应用模型

HarmonyOS支持两种应用模型:FA(Feature Ability)模型和Stage模型。目前主推且会长期演进的模型是Stage模型

两种模型的差异可见:应用模型概况

在DevEco Studio上创建的工程也默认采用Stage模型,其工程结构示意图如下:

工程结构示意图.png

  • AppScope目录由DevEco Studio自动生成,不可更改
  • Module目录名可以由DevEco Studio自动生成(比如entry、library等),也可以自定义。上图中的Module目录名为entry,也是一个entry类型的Module

注意:Module目录名可以自定义,Module的类型是配置文件module.json5中的type字段指定的,跟目录名无关。type可选值为:entryfeatureharshared,分别对应上文的Module类型。

  • 配置文件:
    • AppScope > app.json5app.json5配置文件用于声明应用的全局配置信息,比如应用Bundle名称、应用名称、应用图标、应用版本号等
    • entry > src > main > module.json5module.json5配置文件,用于声明Module基本信息、支持的设备类型、所含的组件信息、运行所需申请的权限等
  • 资源文件:
    • AppScope > resources:用于存放应用需要用到的资源文件,如图形、多媒体、字符串、布局文件等
    • entry > src > main > resources:用于存放该Module需要用到的资源文件,同上
  • entry:
    • src > main > ets:用于存放Module的ArkTS源码文件(.ets文件)
    • src > main > ets > pages:应用/服务包含的页面
    • build-profile.json5:当前的模块信息、编译信息配置项
    • 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,不同类型的Module编译后会生成对应的HAP、HAR、HSP等文件,开发态视图与编译态视图的对照关系如下:

image.png

(全文完)

@nieshuangyan
Copy link

nieshuangyan commented Nov 14, 2024 via email

@Jetmet
Copy link

Jetmet commented Nov 14, 2024 via email

@dwqs
Copy link
Owner Author

dwqs commented Nov 14, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants