HarmonyOS简介
华为开发的全场景分布式智慧操作系统。
五大应用场景:智能家居、智慧办公、智慧出行、健康运动、影音娱乐。
目前最新版本HarmonyOS NEXT预览版本,api11, 需要向华为申请权限。此分享基于api9。
搭建开发环境
需要安装DevEco Studio,参考华为官方说明。
ArkTS
ArkTS = TypeScript + 声明式UI
为什么使用TypeScript
与Android和iOS区分开来,Java/Kotlin、Swift是不宜使用的,TypeScript在前端开发中使用广泛,生态丰富。
为什么使用声明式UI
声明式开发使用起来更加简单方便。相比于React和Vue写UI的方式,简单方便许多,还不需要掌握HTML、CSS。
用过Flutter或者Android Compose都能感受到声明式UI的简单高效。
ArkTS的声明式开发范式
- 装饰器
用来装饰类、结构体、方法以及变量,赋予其特殊的含义,可以理解为Java中的注解。
@Entry表示这是个入口组件,有这个装饰器才能通过路由跳转;
@Component 表示这是个自定义组件;
@State 表示组件中的状态变量,该装饰器修饰的变量的变化会引起UI的变更;
- 自定义组件
可复用的 UI 单元,可组合其它组件,如上述被 @Component 装饰的 struct Hello。
- UI 描述
声明式的方式来描述 UI 的结构,如上述 build() 方法内部的代码块。
- 内置组件
Harmony OS提供了丰富多样的UI组件,让开发者调用, 如Text、Image、TextInput、Button。developer.huawei.com/consumer/cn…
- 事件方法
用于添加组件对事件的响应逻辑,统一通过事件方法进行设置,如跟随在Button后面的onClick()。
- 属性方法
用于组件属性的配置,统一通过属性方法进行设置,如fontSize()、width()、height()、color() 等,可通过链式调用的方式设置多项属性。
了解工程目录文件
工程级目录
- AppScope中存放应用全局所需要的资源文件。
- entry是应用的主模块,存放HarmonyOS应用的代码、资源等。
- oh_modules是工程的依赖包,存放工程依赖的源文件。
- build-profile.json5是工程级配置信息,包括签名、产品配置等。
- hvigorfile.ts是工程级编译构建任务脚本,hvigor是基于任务管理机制实现的一款全新的自动化构建工具,主要提供任务注册编排,工程模型管理、配置管理等核心能力。
- oh-package.json5是工程级依赖配置文件,用于记录引入包的配置信息。
在AppScope,其中有resources文件夹和配置文件app.json5。AppScope>resources>base中包含element和media两个文件夹,
- 其中element文件夹主要存放公共的字符串、布局文件等资源。
- media存放全局公共的多媒体资源文件。
模块级目录
entry>src目录中主要包含总的main文件夹,单元测试目录ohosTest,以及模块级的配置文件。
- main文件夹中,ets文件夹用于存放ets代码,resources文件存放模块内的多媒体及布局文件等,module.json5文件为模块的配置文件。
- ohosTest是单元测试目录。
- build-profile.json5是模块级配置信息,包括编译构建配置项。
- hvigorfile.ts文件是模块级构建脚本。
- oh-package.json5是模块级依赖配置信息文件。
进入src>main>ets目录中,其分为entryability、pages两个文件夹。
- entryability存放ability文件,用于当前ability应用逻辑和生命周期管理。
- pages存放UI界面相关代码文件,初始会生成一个Index页面。
resources目录下存放模块公共的多媒体、字符串及布局文件等资源,分别存放在element、media文件夹中。
app.json5
AppScope>app.json5是应用的全局的配置文件,用于存放应用公共的配置信息。
- bundleName是包名。
- vendor是应用程序供应商。
- versionCode是用于区分应用版本。
- versionName是版本号。
- icon对应于应用的显示图标。
- label是应用名。
module.json5
entry>src>main>module.json5是模块的配置文件,包含当前模块的配置信息。
其中module对应的是模块的配置信息,一个模块对应一个打包后的hap包,hap包全称是HarmonyOS Ability Package,其中包含了ability、第三方库、资源和配置文件。其具体属性及其描述可以参照下表。
| 属性 | 描述 |
|---|---|
| name | 该标签标识当前module的名字,module打包成hap后,表示hap的名称,标签值采用字符串表示(最大长度31个字节),该名称在整个应用要唯一。 |
| type | 表示模块的类型,类型有三种,分别是entry、feature和har。 |
| srcEntry | 当前模块的入口文件路径。 |
| description | 当前模块的描述信息。 |
| mainElement | 该标签标识hap的入口ability名称或者extension名称。只有配置为mainElement的ability或者extension才允许在服务中心露出。 |
| deviceTypes | 该标签标识hap可以运行在哪类设备上,标签值采用字符串数组的表示。 |
| deliveryWithInstall | 标识当前Module是否在用户主动安装的时候安装,表示该Module对应的HAP是否跟随应用一起安装。- true:主动安装时安装。- false:主动安装时不安装。 |
| installationFree | 标识当前Module是否支持免安装特性。- true:表示支持免安装特性,且符合免安装约束。- false:表示不支持免安装特性。 |
| pages | 对应的是main_pages.json文件,用于配置ability中用到的page信息。 |
| abilities | 是一个数组,存放当前模块中所有的ability元能力的配置信息,其中可以有多个ability。 |
对于abilities中每一个ability的属性项,其描述信息如下表。
| 属性 | 描述 |
|---|---|
| name | 该标签标识当前ability的逻辑名,该名称在整个应用要唯一,标签值采用字符串表示(最大长度127个字节)。 |
| srcEntry | ability的入口代码路径。 |
| description | ability的描述信息。 |
| icon | ability的图标。该标签标识ability图标,标签值为资源文件的索引。该标签可缺省,缺省值为空。如果ability被配置为MainElement,该标签必须配置。 |
| label | ability的标签名。 |
| startWindowIcon | 启动页面的图标。 |
| startWindowBackground | 启动页面的背景色。 |
| visible | ability是否可以被其他应用程序调用,true表示可以被其它应用调用, false表示不可以被其它应用调用。 |
| skills | 标识能够接收的意图的action值的集合,取值通常为系统预定义的action值,也允许自定义。 |
| entities | 标识能够接收Want的Entity值的集合。 |
| actions | 标识能够接收的Want的Action值的集合,取值通常为系统预定义的action值,也允许自定义。 |
main_page.json
src/main/resources/base/profile/main_pages.json文件保存的是页面page的路径配置信息,所有需要进行路由跳转的page页面都要在这里进行配置。
UIAbility
应用程序的入口,类似于Android的Application。
一个应用可以有一个UIAbility,也可以有多个UIAbility。每一个UIAbility实例,都对应于一个最近任务列表中的任务。
生命周期
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
// 创建
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
// 应用初始化
}
// 进入前台前会创建WindowStage
onWindowStageCreate(windowStage: window.WindowStage){
// 指定开屏页面
windowStage.loadContent('pages/HomePage', (err, data) => {
});
}
// 进入前台
onForeground(){
// 申请系统需要的资源
}
// 进入后台
onBackground(){
// 释放UI页面不可见时无用的资源,或者在此回调中执行较为耗时的操作
// 例如状态保存等
}
// 在实例销毁前调用
onWindowStageDestroy(){
// 释放UI页面资源
}
onDestroy(){
// 系统资源的释放、数据的保存等
}
}
启动模式
- singleton(单实例模式)
singleton启动模式为单实例模式,也是默认情况下的启动模式。
每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。
- multiton(多实例模式)
multiton启动模式为多实例模式,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。这种情况下可以将UIAbility配置为multiton(多实例模式)。
- specified(指定实例模式)
在startAbility()传入指定key,如果匹配到对应key的实例则利用,否则会创建实例。
在module.json5配置文件中的launchType字段配置启动模式
{
"module": {
// ...
"abilities": [
{
"launchType": "singleton", // multiton、specified
// ...
}
]
}
}
Page
生命周期
@Entry
@Component
struct Hello {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
//在执行其build()函数之前执行。
//允许在aboutToAppear函数中改变状态变量,更改将在后续执行build()函数中生效。
aboutToAppear(){
}
// 页面每次显示时触发一次,
// 包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效。
onPageShow(){
}
// aboutToDisappear函数在自定义组件析构销毁之前执行。
// 不允许在aboutToDisappear函数中改变状态变量
aboutToDisappear(){
}
// 页面每次隐藏时触发一次
// 包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效。
onPageHide(){
}
// 当用户点击返回按钮时触发,仅@Entry装饰的自定义组件生效。
onBackPress(){
}
}
状态管理
ArkUI框架提供了多种管理状态的装饰器来修饰变量,使用这些装饰器修饰的变量即称为状态变量。
当改变状态变量的时候,ArkUI就会自动更新界面中受影响的部分。
| 装饰器 | 场景 |
|---|---|
| @State | 组件内的状态管理 |
| @Prop | 从父组件单向同步状态 |
| @Link | 与父组件双向同步状态 |
| @Provide和@Consume | 跨组件层级双向同步状态 |
页面跳转
导入router模块
import router from '@ohos.router';
方法一:
pushUrl
入参:
| 参数名 | 说明 |
|---|---|
| url | 要跳转的路由,需要在main_pages.json中指定 |
| params | 自定义传参 |
| mode | 可以不传默认,router.RouterMode.Single和router.RouterMode.Standard,默认Standard |
在单实例模式下:如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近同url页面会被移动到栈顶,移动后的页面为新建页,原来的页面仍然存在栈中,页面栈的元素数量不变;如果目标页面的url在页面栈中不存在同url页面,按多实例模式跳转,页面栈的元素数量会加1。
router.pushUrl({
url: 'pages/Second',
params: {
src: 'Index页面传来的数据',
}
}, router.RouterMode.Single)
方法二:
replaceUrl
在单实例模式下:如果目标页面的url在页面栈中已经存在同url页面,离栈顶最近同url页面会被移动到栈顶,替换当前页面,并销毁被替换的当前页面,移动后的页面为新建页,页面栈的元素数量会减1;如果目标页面的url在页面栈中不存在同url页面,按多实例模式跳转,页面栈的元素数量不变。
router.replaceUrl({
url: 'pages/Second',
params: {
src: 'Index页面传来的数据',
}
}, router.RouterMode.Single)
接收参数
通过调用router.getParams()方法获取页面传递过来的自定义参数。
import router from '@ohos.router';
@Entry
@Component
struct Second {
@State src: string = (router.getParams() as Record<string, string>)['src'];
}
页面返回和参数接收
// 返回
router.back();
// 返回指定页面
router.back({ url: 'pages/Index' });
// 返回指定页面并传参
router.back({
url: 'pages/Index',
params: {
src: 'Second页面传来的数据',
}
})
接收返回参数:
也是通过router.getParams()接收。
import router from '@ohos.router';
class routerParams {
src:string
constructor(str:string) {
this.src = str
}
}
@Entry
@Component
struct Index {
@State src: string = '';
onPageShow() {
this.src = (router.getParams() as routerParams).src
}
}
网络请求
在模块的module.json5中声明网络访问权限
{
"module" : {
"requestPermissions":[
{
"name": "ohos.permission.INTERNET"
}
]
}
}
发起网络请求
// 导入http模块
//import http from '@ohos.net.http'; //api9
import { http } from '@kit.NetworkKit'; //api11
let httpRequest = http.createHttp();
// 发起Post请求
let url = "https://EXAMPLE_URL";
let promise = httpRequest.request(
// 请求url地址
url,
{
// 请求方式
method: http.RequestMethod.POST,
// 请求的额外数据。
extraData: {
"param1": "value1",
"param2": "value2",
},
// 可选,默认为60s
connectTimeout: 60000,
// 可选,默认为60s
readTimeout: 60000,
// 开发者根据自身业务需要添加header字段
header: {
'Content-Type': 'application/json'
}
});
// 处理请求结果
promise.then((data) => {
if (data.responseCode === http.ResponseCode.OK) {
console.info('Result:' + data.result);
console.info('code:' + data.responseCode);
}
}).catch((err) => {
console.info('error:' + JSON.stringify(err));
});
注:三方库axios。