【鸿蒙HarmonyOS】深入理解一端开发,多端部署
一端开发,多端部署
1.简介
背景
随着终端设备形态日益多样化,分布式技术逐渐打破单一硬件边界,一个应用或服务,可以在不同的硬件设备之间随意调用、互助共享,让用户享受无缝的全场景体验。而作为应用开发者,广泛的设备类型也能为应用带来广大的潜在用户群体。但是如果一个应用需要在多个设备上提供同样的内容,则需要适配不同的屏幕尺寸和硬件,开发成本较高。HarmonyOS系统面向多终端提供了“一次开发,多端部署”(后文中简称为“一多”)的能力,让开发者可以基于一种设计,高效构建多端可运行的应用。
定义及目标
定义:一套代码工程,一次开发上架,多端按需部署。
目标:支撑开发者快速高效的开发支持多种终端设备形态的应用,实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。
为了实现“一多”的目标,需要解决两个基础问题:
- 不同设备间的屏幕尺寸、色彩风格等存在差异,页面如何适配。
- 不同设备的系统能力有差异,如智能穿戴设备是否具备定位能力、智慧屏是否具备摄像头等,功能如何兼容。
从第4章开始将从UX设计、系统能力等角度,详尽的解答上述问题。
说明
- 应用开发不仅包含应用页面开发,还包括应用后端功能开发以及服务器端开发等。
- 本文旨在指导开发者如何开发“一多”应用,服务器端开发不在本文探讨范围内。
基础知识
为了更好的阅读后面的章节,本小节主要介绍了一些基础知识,方便读者理解内容。
方舟开发框架
方舟开发框架(简称:ArkUI)提供开发者进行应用UI开发时所必须的能力。
方舟开发框架提供了两种开发范式,分别是基于JS扩展的类Web开发范式(后文中简称为“类Web开发范式”)和基于ArkTS的声明式开发范式(后文中简称为“声明式开发范式”)。
- 声明式开发范式:采用TS语言并进行声明式UI语法扩展,从组件、动效和状态管理三个维度提供了UI绘制能力。UI开发更接近自然语义的编程方式,让开发者直观地描述UI界面,不必关心框架如何实现UI绘制和渲染,实现极简高效开发。同时,选用有类型标注的TS语言,引入编译期的类型校验,更适用大型的应用开发。
- 类Web开发范式:采用经典的HML、CSS、JavaScript三段式开发方式。使用HML标签文件进行布局搭建,使用CSS文件进行样式描述,使用JavaScript文件进行逻辑处理。UI组件与数据之间通过单向数据绑定的方式建立关联,当数据发生变化时,UI界面自动触发更新。此种开发方式,更接近Web前端开发者的使用习惯,快速将已有的Web应用改造成方舟开发框架应用。主要适用于界面较为简单的中小型应用开发。
两种开发范式的对比如下。
开发范式名称 | 语言生态 | UI更新方式 | 适用场景 | 适用人群 |
声明式开发范式 | ArkTS语言 | 数据驱动更新 | 复杂度较大、团队合作度较高的程序 | 移动系统应用开发人员、系统应用开发人员 |
类Web开发范式 | JS语言 | 数据驱动更新 | 界面较为简单的中小型应用和卡片 | Web前端开发人员 |
说明
声明式开发范式占用内存更少,更推荐开发者选用声明式开发范式来搭建应用UI界面。
应用程序包结构
在进行应用开发时,一个应用通常包含一个或多个Module。Module是应用/服务的基本功能单元,包含了源代码、资源文件、第三方库及应用/服务配置文件,每一个Module都可以独立进行编译和运行。
Module分为“Ability”和“Library”两种类型:
- “Ability”类型的Module编译后生成HAP包。
- “Library”类型的Module编译后生成HAR包或HSP包。
应用以APP Pack形式发布,其包含一个或多个HAP包。HAP是应用安装的基本单位,HAP可以分为Entry和Feature两种类型:
- Entry类型的HAP:应用的主模块。在同一个应用中,同一设备类型只支持一个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。
- Feature类型的HAP:应用的动态特性模块。Feature类型的HAP通常用于实现应用的特性功能,一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含。
说明
关于Entry类型的HAP包、Feature类型的HAP包、HAR包、HSP包以及APP Pack的详细介绍请参考应用程序包结构说明 。
部署模型
“一多”有两种部署模型:
- 部署模型A:不同类型的设备上按照一定的工程结构组织方式,通过一次编译生成相同的HAP(或HAP组合)。
- 部署模型B:不同类型的设备上按照一定的工程结构组织方式,通过一次编译生成不同的HAP(或HAP组合)。
开发者可以从应用UX设计及应用功能两个维度,结合具体的业务场景,考虑选择哪种部署模型。当然,也可以借助设备类型分类,快速做出判断。
从屏幕尺寸、输入方式及交互距离三个维度考虑,可以将常用类型的设备分为不同泛类:
- 手机、平板
- 车机、智慧屏
- 智能穿戴
- ……
说明
当应用完成一多能力适配后,在手机与车机协同场景,使用超级桌面将手机应用流转到车机屏幕上,无需重新适配即可兼容车机屏幕显示。
对于相同泛类的设备,优先选择部署模型A,对于不同泛类设备,优先选择部署模型B。
说明
- 应用在不同泛类设备上的UX设计或功能相似时,可以使用部署模型A。
- 应用在同一泛类不同类型设备上UX设计或功能差异非常大时,可以使用部署模型B,但同时也应审视应用的UX设计及功能规划是否合理。
- 本小节引入部署模型A和部署模型B的概念是为了方便开发者理解。实际上在开发多设备应用时,如果目标设备类型较多,往往是部署模型A和部署模型B混合使用。
- 不管采用哪种部署模型,都应该采用一次编译。
工程结构
“一多”推荐在应用开发过程中使用如下的“三层工程结构”。
- common(公共能力层):用于存放公共基础能力集合(如工具库、公共配置等)。
common层可编译成一个或多个HAR包或HSP包(HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝;而HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份),其只可以被products和features依赖,不可以反向依赖。
- features(基础特性层):用于存放基础特性集合(如应用中相对独立的各个功能的UI及业务逻辑实现等)。
各个feature高内聚、低耦合、可定制,供产品灵活部署。不需要单独部署的feature通常编译为HAR包或HSP包,供products或其它feature使用,但是不能反向依赖products层。需要单独部署的feature通常编译为Feature类型的HAP包,和products下Entry类型的HAP包进行组合部署。features层可以横向调用及依赖common层。
- products(产品定制层):用于针对不同设备形态进行功能和特性集成。
products层各个子目录各自编译为一个Entry类型的HAP包,作为应用主入口。products层不可以横向调用。
代码工程结构抽象后一般如下所示:
/application
├── common # 可选。公共能力层, 编译为HAR包或HSP包
├── features # 可选。基础特性层
│ ├── feature1 # 子功能1, 编译为HAR包或HSP包或Feature类型的HAP包
│ ├── feature2 # 子功能2, 编译为HAR包或HSP包或Feature类型的HAP包
│ └── ...
└── products # 必选。产品定制层├── wearable # 智能穿戴泛类目录, 编译为Entry类型的HAP包├── default # 默认设备泛类目录, 编译为Entry类型的HAP包└── ...
说明
- 部署模型不同,相应的代码工程结构也有差异。部署模型A和部署模型B的主要差异点集中在products层:部署模型A在products目录下同一子目录中做功能和特性集成;部署模型B在products目录下不同子目录中对不同的产品做差异化的功能和特性集成。
- 开发阶段应考虑不同类型设备间最大程度的复用代码,以减少开发及后续维护的工作量。
- 整个代码工程最终构建出一个APP包,应用以APP包的形式发布到应用市场中。
2.界面级一多
1.自适应布局
@Entry
@Component
struct Demo01 {// 绑定的宽度-默认 600@State containerWidth: number = 600// 底部滑块,可以通过拖拽滑块改变容器尺寸。@BuildersliderBuilder() {Slider({value: this.containerWidth, // 绑定的值min: 400, // 最小值max: 1000, // 最大值style: SliderStyle.OutSet // 滑块在滑轨上}).onChange((value: number) => {this.containerWidth = value}).blockColor(Color.White).width('60%').position({ x: '20%', y: '80%' })}build() {Stack({ alignContent: Alignment.TopStart }) {// 标记现在的宽度Text('宽度:' + this.containerWidth).zIndex(2).translate({ x: 20, y: 20 }).fontColor(Color.Orange)// 核心区域Column() {Column() {Row() {// 布局能l力 1:拉伸能力:// 容器组件尺寸发生改变时,将变化的部分分配给容器内的【指定区域】//// 涉及属性:// flexShrink:压缩比例,默认值:Column,Row 时(0),Flex 时(1)// flexGrow:拉伸比例,默认值 0// 需求:// 1. 空间不足时:分配给左右,1:1// 2. 空间富余时:分配给中间// 左Row() {Text('左').fontSize(20).fontColor(Color.White)}.justifyContent(FlexAlign.Center).width(150).height(400).backgroundColor('#c2baa6').flexShrink(1)// 中Row() {Text('中').fontSize(30).fontColor(Color.White)}.width(300).height(400).backgroundColor('#68a67d').justifyContent(FlexAlign.Center).flexGrow(1)// 右Row() {Text('右').fontSize(20).fontColor(Color.White)}.justifyContent(FlexAlign.Center).width(150).height(400).backgroundColor('#c2baa6').flexShrink(1)}.width(this.containerWidth).justifyContent(FlexAlign.Center).alignItems(VerticalAlign.Center).border({ width: 2, color: Color.Orange }).backgroundColor(Color.Black)}// 底部滑块this.sliderBuilder()}.width('100%').height('100%').backgroundColor('#F1F3F5').justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}}
}
export interface NavItem {id: numbericon: ResourceStrtitle: string
}@Entry
@Component
struct Demo02 {readonly list: NavItem [] = [{ id: 1, icon: $r('app.media.ic_reuse_01'), title: '淘金币' },{ id: 2, icon: $r('app.media.ic_reuse_02'), title: '摇现金' },{ id: 3, icon: $r('app.media.ic_reuse_03'), title: '闲鱼' },{ id: 4, icon: $r('app.media.ic_reuse_04'), title: '中通快递' },]@State rate: number = 600// 底部滑块,可以通过拖拽滑块改变容器尺寸@BuildersliderBuilder() {Slider({value: this.rate,min: 200,max: 600,style: SliderStyle.OutSet}).onChange((value: number) => {this.rate = value}).blockColor(Color.White).width('60%').position({ x: '20%', y: '80%' })}build() {Stack({ alignContent: Alignment.TopStart }) {// 标记现在的宽度Text('宽度:' + this.rate.toFixed(0)).zIndex(2).translate({ x: 20, y: 20 }).fontColor(Color.Orange)Column() {Column() {// 布局能力 2:均分能力// 指容器组件尺寸发生变化时,增加或减小的空间均匀分配给容器组件内所有【空白区域】。// 常用于内容数量固定、均分显示的场景,比如工具栏、底部菜单栏、导航栏等// 涉及属性:// Row、Column、Flex 组件的 justifyContent 属性// justifyContent设置为 FlexAlign.SpaceEvenly即可Row() {ForEach(this.list, (item: NavItem) => {Column({ space: 8 }) {Image(item.icon).width(48).height(48)Text(item.title).fontSize(12)}.justifyContent(FlexAlign.Center).width(80).height(102).backgroundColor('#8FBF9F').borderRadius(10)})}.width('100%').justifyContent(FlexAlign.SpaceEvenly) // 均分}.width(this.rate) // 绑定滑块改变的尺寸.padding({ top: 10, bottom: 10 }).backgroundColor(Color.White).borderRadius(16)this.sliderBuilder()}.width('100%').height('100%').backgroundColor(Color.Pink).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}}
}
3. 占比能力
占比能力是指子组件的【宽高】按照【预设的比例】,随父容器组件发生变化。实现的方式有 2 种:
- 宽高设置为百分比
- 设置 layoutWeight
4.缩放能力
缩放能力是指子组件的【宽高】按照预设的比例,随容器组件发生变化,变化过程中子组件的【宽高比不变】。使用的属性是 aspectRatio
延伸能力
延伸能力是指容器组件内的子组件,按照其在列表中的先后顺序,随容器组件尺寸变化【显示或隐藏】,隐藏时可以通过滑动切换显示。实现的方式是通过 List 组件或 Scroll 组件
隐藏能力
隐藏能力指的是:按其【显示优先级】,随容器组件尺寸变化显示或隐藏。通过displayPriority属性来实现
属性名 | 类型 | 必填 | 说明 |
displayPriority | number | 是 | 设置当前组件在布局容器中显示的优先级,当父容器空间不足时,低优先级的组件会被隐藏。 |
折行能力
折行能力是指容器组件尺寸发生变化,当布局方向尺寸不足以显示完整内容时自动换行。折行能力通过使用 Flex 折行布局 (将wrap属性设置为FlexWrap.Wrap)实现。
名称 | 描述 |
NoWrap | Flex容器的元素单行/列布局,子项不允许超出容器。 |
Wrap | Flex容器的元素多行/列排布,子项允许超出容器。 |
WrapReverse | Flex容器的元素反向多行/列排布,子项允许超出容器。 |
小结
2.响应式布局
响应式布局能力 | 简介 |
断点 | 将窗口宽度划分为不同的范围(即断点),监听窗口尺寸变化,当断点改变时同步调整页面布局。 |
媒体查询 | 媒体查询支持监听窗口宽度、横竖屏、深浅色、设备类型等多种媒体特征,当媒体特征发生改变时同步调整页面布局。 |
栅格布局 | 栅格组件将其所在的区域划分为有规律的多列,通过调整不同断点下的栅格组件的参数以及其子组件占据的列数等,实现不同的布局效果。 |
1.断点
@ohos.display (屏幕属性)
屏幕属性提供管理显示设备的一些基础能力,包括获取默认显示设备的信息,获取所有显示设备的信息以及监听显示设备的插拔行为。
display.getDefaultDisplaySync9+
getDefaultDisplaySync(): Display
获取当前默认的display对象。
densityPixels
显示设备逻辑像素的密度
// MainAbility.ts
import { window, display } from '@kit.ArkUI'
import { UIAbility } from '@kit.AbilityKit'export default class MainAbility extends UIAbility {private curBp: string = ''//...// 根据当前窗口尺寸更新断点private updateBreakpoint(windowWidth: number) :void{try {// 核心代码1: 将长度的单位由px换算为vp,(px除以像素密度得到vp)let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixelslet newBp: string = ''// 核心代码2: 基于窗口宽度vp值,判断当前设备属于哪个断点范围if (windowWidthVp < 320) {newBp = 'xs'} else if (windowWidthVp < 600) {newBp = 'sm'} else if (windowWidthVp < 840) {newBp = 'md'} else {newBp = 'lg'}if (this.curBp !== newBp) {this.curBp = newBp// 核心代码3: 使用状态变量记录当前断点值AppStorage.setOrCreate('currentBreakpoint', this.curBp)}} catch(err) {console.log("getDisplayByIdSync failed err" + err.code)}} onWindowStageCreate(windowStage: window.WindowStage) :void{windowStage.getMainWindow().then((windowObj) => {// 获取应用启动时的窗口尺寸this.updateBreakpoint(windowObj.getWindowProperties().windowRect.width)// 注册回调函数,监听窗口尺寸变化windowObj.on('windowSizeChange', (windowSize)=>{this.updateBreakpoint(windowSize.width)})});// ...}//...
}
系统工具-BreakPointType
- 如果是 两种的情况:用 三元表达式 即可
- 如果是 多种的情况:用 三元表达式 就不太方便啦
// 根据屏幕尺寸返回相应的朝着
interface BreakPointTypeOption<T> {xs?: Tsm?: Tmd?: Tlg?: T
}export class BreakPointType<T> {options: BreakPointTypeOption<T>constructor(option: BreakPointTypeOption<T>) {this.options = option}getValue(currentBreakPoint: string) {if (currentBreakPoint === 'xs') {return this.options.xs} else if (currentBreakPoint === 'sm') {return this.options.sm} else if (currentBreakPoint === 'md') {return this.options.md} else if (currentBreakPoint === 'lg') {return this.options.lg} else {return undefined}}
}
import { BreakPointType } from "./breakpointSystem"@Entry
@Component
export struct Demo {@State rate: number = 100@StorageProp('currentBreakpoint') curBp:string='sm'// 底部滑块,可以通过拖拽滑块改变容器尺寸@Builderslider() {Slider({value: this.rate,//当前进度值。min: 0,max: 400,style: SliderStyle.OutSet// 设置Slider的滑块与滑轨显示样式。}).blockColor(Color.White).width('60%').height(50).onChange((value: number) => {this.rate = value}).position({ x: '20%', y: '80%' })}build() {Column(){Text('宽度:'+this.rate.toFixed(0)).margin({bottom:30})Text('屏幕尺寸:'+this.curBp).margin({bottom:30})Row(){Image($r("app.media.ic_tabbar_icon_0_selected")).width(50).displayPriority(1)Image($r("app.media.ic_tabbar_icon_1_selected")).width(50).displayPriority(2)Image($r("app.media.ic_tabbar_icon_2_selected")).width(50).displayPriority(3)Image($r("app.media.ic_tabbar_icon_3_selected")).width(50).displayPriority(4)}.width(new BreakPointType({sm: 100,md: 200,lg: 400}).getValue(this.curBp)).backgroundColor(Color.Gray)this.slider()}.width('100%').height("100%").backgroundColor(Color.Pink)}
}
2.媒体查询
媒体查询常用于下面两种场景:
- 针对设备和应用的属性信息(比如显示区域、深浅色、分辨率),设计出相匹配的布局。
- 当屏幕发生动态改变时(比如分屏、横竖屏切换),同步更新应用的页面布局。
相比于上一节演示的 通过窗口对象监听尺寸变化,媒体查询的功能会【更为强大】
只需要查询断点值:【获取窗口对象并监听窗口尺寸】
还要额外查询其他设备信息,【媒体查询】
// 导入模块
import mediaquery from '@ohos.mediaquery';// 1. 创建监听器
const listenerXS: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(0vp<=width<320vp)');
const listenerSM: mediaquery.MediaQueryListener = mediaquery.matchMediaSync('(320vp<=width<600vp)');// 2. 注册监听器
aboutToAppear(): void {// 添加回调函数listenerXS.on('change', (res: mediaquery.MediaQueryResult) => {console.log('changeRes:', JSON.stringify(res))// 执行逻辑})listenerSM.on('change', (res: mediaquery.MediaQueryResult) => {console.log('changeRes:', JSON.stringify(res))// 执行逻辑})
}// 4. 移除监听器
aboutToDisappear(): void {// 移除监听 避免性能浪费listenerXS.off('change')listenerSM.off('change')
}
3.栅格布局
创建网格
栅格组件的本质是:将组件划分为有规律的多列,通过调整【不同断点】下的【栅格组件的列数】,及【子组件所占列数】实现不同布局
通过调整总列数,及子组件所占列数,实现不同布局
断点:
断点名称 | 取值范围(vp) | 设备 |
xs | [0, 320) | 手表等超小屏 |
sm | [320, 600) | 手机竖屏 |
md | [600, 840) | 手机横屏,折叠屏 |
lg | [840, +∞) | 平板,2in1 设备 |
@Entry
@Component
struct Demo10 {@State breakPoint: string = 'sm'// 颜色数组build() {Column() {// GridRow 默认支持 4 个断点// xs:(0vp<=width<320vp) 智能穿戴,比如手表// sm:(320vp<=width<600vp) 手机// md:(600vp<=width<840vp) 折叠屏// lg:(840vp<=width) 平板GridRow({// 4个断点 和默认的一样breakpoints: { value: ['320vp', '600vp', '840vp'] },gutter: 10, // 子组件间隙// columns: 12 // 统一设计列数 默认 12columns: {// 不同的断点分别设置不同的列数xs: 2, // 超小sm: 4, // 手机竖屏md: 8, // 折叠,手机横屏lg: 12 // 大屏}}) {ForEach(Array.from({ length: 20 }), (item: string, index: number) => {GridCol({// 每一行 2 个子元素,span 怎么设置(占的行数)// span: 2, // 占用列数 这样设置所有断点都是 2 列// 支持不同断点分别设置不同的占用列数// span: {// xs: 2,// sm: 4,// md: 6,// lg: 8// },// offset 偏移列数 默认为 0// offset: 1, // 偏移一列// 支持不同断点分别设置偏移不同的列数offset: {// xs: 2,// sm: 1}}) {Text(index.toString()).height(50)}.border({ width: 1 })})}.border({ width: 1, color: Color.Orange }).width('90%').height('90%')// 断点发生变化时触发回调// breakpoint当前的断点 字符串.onBreakpointChange(breakpoint => {console.log('breakpoint:', breakpoint)this.breakPoint = breakpoint})Text(this.breakPoint).width('100%').textAlign(TextAlign.Center).fontSize(30).fontWeight(900)}.width('100%').height('100%')}
}
2.功能级别一多
功能开发的适配主要体现在需要适配不同范类的应用,比如既要适配手机和平板,也需要适配智能穿戴设备,如果是同泛类产品,系统能力一致,无需考虑多设备上应用功能开发的差异,我们的美寇商城需要适配的是手机和Pad,属于同泛类产品,无需考虑功能开发的差异。以下是常见类型分类:
- 默认设备(一般为手机)、平板
- 车机、智慧屏
- 智能穿戴
什么是系统能力
系统能力(即SystemCapability,缩写为SysCap)指操作系统中每一个相对独立的特性,如蓝牙,WIFI,NFC,摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失。
比如:display.isFoldable() 这个 api 并不是每个设备都可以使用,在调用之前就可以先判断一下
如何适配系统能力
- 不同设备的系统能力有差异,如智能穿戴设备是否具备定位能力、智慧屏是否具备摄像头等,功能如何兼容。
if (canIUse("能力集的名字")) {// 正常调用
} else {// 提示用户console.log("该设备不支持SystemCapability.Communication.NFC.Core")
}
import controller from '@kit.ConnectivityKit';
try {controller.enableNfc();console.log("controller enableNfc success");
} catch (busiError) {console.log("controller enableNfc busiError: " + busiError);
}
注意:
- 目前的开发主要是 手机及平板开发,属于统一范类,功能差别不大
- 目前 Harmony Next 的系统首发登录的肯定是手机,其他设备会逐步接入
3.工程级一多
HarmonyOS应用的分层架构设计以一套代码工程为基础,旨在为华为的手机、2in1等1+8全场景设备提供支持,实现了“一次开发,多端部署”的开发理念。
HarmonyOS应用的分层架构主要包括三个层次:产品定制层、基础特性层和公共能力层,为开发者构建了一个清晰、高效、可扩展的设计架构。
- 产品定制层
产品定制层专注于满足不同设备或使用场景(如应用)的个性化需求,包括UI设计、资源和配置,以及针对特定场景的交互逻辑和功能特性。
产品定制层的功能模块独立运作,同时依赖基础特性层和公共能力层来实现具体功能。
作为应用的入口,产品定制层是用户直接互动的界面。为满足特定产品需求,产品定制层可灵活地调整和扩展,从而满足各种使用场景。
- 基础特性层
基础特性层位于公共能力层之上,用于存放基础特性集合,例如相对独立的功能UI和业务逻辑实现。该层的每个功能模块都具有高内聚、低耦合、可定制的特点,以支持产品的灵活部署。
基础特性层为上层的产品定制层提供稳健且丰富的基础功能支持,包括UI组件、基础服务等。同时依赖于下层的公共能力层为其提供通用功能和服务。
为了增强系统的可扩展性和维护性,基础特性层将功能进行模块化处理。例如,一个应用的底部导航栏中的每个选项都可能是一个独立的业务模块。
- 公共能力层
公共功能层用于存放公共基础能力,集中了例如公共UI组件、数据管理、外部交互以及工具库等的共享功能。应用可以共享和调用这些公共能力。
公共能力层为上层的基础特性层和产品定制层提供稳定可靠的功能支持,确保整个应用的稳定性和可维护性。
公共能力层包括但不限于以下组成:
-
- 公共UI组件:这些组件被设计成通用且高度可复用的,确保在不同的应用程序模块间保持一致的用户体验。公共UI组件提供了标准化且友好的界面,帮助开发者快速实现常见的用户交互需求,例如提示、警告、加载状态显示等,从而提高开发效率和用户满意度。
- 数据管理:负责应用程序中数据的存储和访问,包括应用数据、系统数据等,提供了统一的数据管理接口,简化数据的读写操作。通过集中式的数据管理方式不仅使得数据的维护更为简单,而且能够保证数据的一致性和安全性。
- 外部交互:负责应用程序与外部系统的交互,包括网络请求、文件I/O、设备I/O等,提供统一的外部交互接口,简化应用程序与外部系统的交互。开发者可以更为方便地实现应用程序的网络通信、数据存储和硬件接入等功能,从而加速开发流程并保证程序的稳定性和性能。
- 工具库:提供一系列常用工具函数和类,例如字符串处理、日期时间处理、加密解密、数据压缩解压等,帮助开发者提高效率和代码质量。
HAP、HAR、HSP三者的功能和使用场景总结对比如下:
Module类型 | 包类型 | 说明 |
Ability | HAP | 应用的功能模块,可以独立安装和运行,必须包含一个entry类型的HAP,可选包含一个或多个feature类型的HAP。 |
Static Library | HAR | 静态共享包,编译态复用。 - 支持应用内共享,也可以发布后供其他应用使用。 - 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。 - 作为三方库,发布到OHPM中心仓,供其他应用使用。 - 多包(HAP/HSP)引用相同的HAR时,会造成多包间代码和资源的重复拷贝,从而导致应用包膨大。 - 注意:编译HAR时,建议开启混淆能力,保护代码资产。 |
Shared Library | HSP | 动态共享包,运行时复用。 - 当前仅支持应用内共享。 - 当多包(HAP/HSP)同时引用同一个共享包时,采用HSP替代HAR,可以避免HAR造成的多包间代码和资源的重复拷贝,从而减小应用包大小。 |