一、前言
HarmonyOS的共享包相当于Android的Library,在HarmonyOS中,给开发者提供了两种共享包,HAR(Harmony Archive)静态共享包,和HSP(Harmony Shared Package)动态共享包
区别:
- 静态共享包中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝
- 动态共享包中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份
动态共享包解决的问题:
- 多个使用方引用相同的共享包,导致的APP包大小膨胀问题
- 多个使用方引用相同的共享包,共享包中的一些状态变量无法共享的问题
两者的使用场景:
- 仅用于应用内部代码、资源的共享,建议使用动态共享包
- 作为应用模块的依赖项被引用,就可以选择静态共享包
二、共享包的创建
File -> New -> Module
有两种共享包,一种是一种是动态共享包Shared Library,一种是静态共享包Static Library
选择一种共享包,点击Next
设置共享包的name,点击Finish,即可完成共享包的创建
动态共享包创建完毕之后,其type类型为【shared】,如果你创建的是静态共享包,这里的类型就是【har】
**三、**动态共享包添加依赖
动态共享包创建完成之后,有两个Module,主Module【entry】和动态共享包【Common】
如何在【entry】中调用【Common】模块中的代码和资源文件等,需要让【entry】关联依赖上【Common】
要使用HSP中的接口,首先需要在使用方的oh-package.json5中配置对它的依赖
1、依赖方式一
在【entry】中的oh-package.json5 文件中,在dependencies中引入共享包【Common】
引入格式:
'folder ': "file:../folder"
描述:
- 后面的folder就是创建的共享包名称,比如这里的Common
- 前面的folder是用来生成的映射目录,可与共享包名称,也可以自行定义
添加了之后需要同步项目,点击【Sync Now 】即可,也可以点击提示信息下的【Run 'ohpm install'】
同步之后,该模块就会被映射到Module【entry】的oh_modules目录下
2、依赖方式二
也可以使用@xx/xx来添加依赖
引入格式:
"@xx/xx": "file:../Common"
比如:import { add } from '@ohos/Common'
同步之后会在oh_modules目录下创建ohos>Common
3、依赖方式三
除了以上手动方式添加依赖之外,也可以使用命令的方式进行实现,命令行中或在IDE Terminal窗口中,目录进入到主模块下,执行ohpm install ../folder 命令,folder是你的共享包
apple@lengfeng MyApplication % cd entry
apple@lengfeng entry % ohpm install ../Common
**注意:**需要提前配置ohpm
命令执行完毕之后,也会自动的在oh-package.json5里添加依赖,并生成映射文件
四、动态共享包调用方式
1.方法调用
在创建的共享包Common中,有一个自动创建一个工具类Calc,里面有一个求和方法add
使用的时候需要引入:
import { add } from 'common/src/main/ets/utils/Calc'
//或者第二种依赖方式导包
import { add } from '@ohos/Common'
使用如下:
TypeScript
import { add } from 'common/src/main/ets/utils/Calc'
//或者
//import { add } from '@ohos/Common'
//或者
//import { add } from 'common'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
var result = add(1, 2)
this.message = "result=" + result
})
}
.width('100%')
}
.height('100%')
}
}
案例中是给一个Text增加一个点击事件,然后调用【Common】中的Calc类的add方法,返回一个结果,并显示更新Text
预览是一片空白的,没关系,直接运行到模拟器,或者HarmonyOS手机上查看,没想到报错了
根据报错信息提示:未执行部署多个包,由于增加了一个【Common】共享包,解决方案是
选择上面运行模块,选择Edit Configurations
在Deploy Multi Hap 标签页,勾选**Deploy Multi Hap Packages,**然后选择新增的共享包即可,将entry和library模块一起运行
然后就能正常运行了
点击文本,就能看到计算后的结果了
2.类的调用
调用共享包中的工具类
在共享包中定义一个工具类Log.ts,需要用export关键字进行对外暴露
TypeScript
export class Log {
static i(msg: string) {
console.info(msg)
}
static e(msg: string) {
console.error(msg)
}
}
然后在【common】的Index添加导出Log,对外暴露的接口,需要在入口文件index.ets中声明
在【entry】中导入类,就可以直接使用了
TypeScript
import { add,Log } from 'common'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
var result = add(1, 2)
this.message = "result=" + result
Log.i("----------------")
})
}
.width('100%')
}
.height('100%')
}
}
点击Text后打印出日志:
3.组件的调用
组件的调用跟类的调用一致
先export导出,然后import导入
TypeScript
@Component
export struct CommonText {
@State message: string = '我是Common模块中的组件'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor(Color.Red)
}
.width('100%')
}
}
}
TypeScript
import { add, Log, CommonText } from 'common'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
var result = add(1, 2)
this.message = "result=" + result
Log.info("----------------")
})
CommonText()
}
.width('100%')
}
.height('100%')
}
}
效果图:
4.页面路由跳转
想在【entry】模块中,添加一个按钮跳转至【Common】模块中的CommonIndex页面(路径为:Common/src/main/ets/pages/CommonIndex.ets),那么可以在使用方的代码(entry模块下的Index.ets,路径为:entry/src/main/ets/pages/Index.ets)里这样使用:
TypeScript
import { add, Log, CommonText } from 'common'
import router from '@ohos.router'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
var result = add(1, 2)
this.message = "result=" + result
Log.info("----------------")
})
CommonText()
.onClick(() => {
router.pushUrl({ url: '@bundle:com.lf.myapplication/Common/ets/pages/CommonIndex' })
.then(() => {
console.log("push page success");
})
.catch(err => {
console.error(`pushUrl failed, code is ${err.code}, message is ${err.message}`);
})
})
}
.width('100%')
}
.height('100%')
}
}
其中router.pushUrl方法的入参中url的内容为:
'@bundle:com.lf.myapplication/Common/ets/pages/CommonIndex'
内容格式为:
'@bundle:包名(bundleName)/模块名(moduleName)/路径/页面所在的文件名(不加.ets后缀)'
包名的查看路径:
AppScope -> app.json5中可以查看app的信息,包括包名,版本code、name等
效果图:
点击 CommonText()组件跳转到【Common】模块中的CommonIndex页面
5.资源访问
创建资源目录:
选择模块名 > 右击 > New > Resource File
- 可同时创建资源目录和资源文件,文件默认创建在base目录的对应资源组。如果选择了限定词,则会按照命名规范自动生成限定词和资源组目录,并将文件创建在限定词目录中
- 图中File name为需要创建的文件名。Resource type为资源组类型,默认是element。Root Element为资源类型。Avaliable qualifiers为供选择的限定词目录,通过右边的小箭头可添加或者删除
创建资源文件
在资源目录(element、media、profile)的右键菜单选择"New > XXX Resource File",即可创建对应资源组目录的资源文件。例如,在element目录下可新建Element Resource File
资源包内资源访问
-
对于应用资源,在工程中,通过"$r('app.type.name')"形式引用。其中,app为应用内resources目录中定义的资源;type为资源类型或资源的存放位置,取值包含"color"、"float"、"string"、"plural"、"media";name为资源命名,由开发者定义资源时确定
-
对于rawfile目录资源,通过"$rawfile('filename')"形式引用。其中,filename为rawfile目录下文件的相对路径,文件名需要包含后缀,路径开头不可以以"/"开头
-
对于rawfile目录的descriptor,可通过资源管理的getRawId接口引用,其返回值descriptor.fd为hap包的fd。此时,访问rawfile文件需要结合{fd, offset, length}一起使用
TypeScript
Text($r('app.string.string_hello'))
.fontColor($r('app.color.color_hello'))
.fontSize($r('app.float.font_hello'))
//引用string.json资源。Text中$r的第一个参数指定string资源,第二个参数用于替换string.json文件中的%s
//"这是原文本,可以拼接一个:%s"
Text($r('app.string.message_arrive', "拼接文本"))
// media资源的$r引用
Image($r('app.media.my_background_image'))
// rawfile$r引用rawfile目录下图片
Image($rawfile('test.png'))
// rawfile$r引用rawfile目录下图片
Image($rawfile('newDir/newTest.png'))
- 对于系统资源,可以通过"$r('sys.type.resource_id')"的形式引用。其中,sys为系统资源;type为资源类型,取值包括"color"、"float"、"string"、"media";resource_id为资源id
TypeScript
Text('Hello')
.fontColor($r('sys.color.ohos_id_color_emphasize'))
.fontSize($r('sys.float.ohos_id_text_size_headline1'))
.fontFamily($r('sys.string.ohos_id_text_font_family_medium'))
.backgroundColor($r('sys.color.ohos_id_color_palette_aux1'))
Image($r('sys.media.ohos_app_icon'))
.border({
color: $r('sys.color.ohos_id_color_palette_aux1'),
radius: $r('sys.float.ohos_id_corner_radius_button'), width: 2
})
.margin({
top: $r('sys.float.ohos_id_elements_margin_horizontal_m'),
bottom: $r('sys.float.ohos_id_elements_margin_horizontal_l')
})
.height(200)
.width(300)
跨包访问资源
**注意:**HarmonyOS目前还不支持资源跨包调用
如果确实需要使用共享包中的资源,可以另辟蹊径。
既然资源不能暴露,但是类和方法可以暴露,也就是可以把资源中的属性,通过工具类做一层中转即可
1.定义资源
在【Common】中有一个字符串资源message_arrive,想要在【entry】中使用,如何实现呢?
2.定义工具类
定义一个工具类中转 StringUtil.ets,就可以拿到对应的资源文件了
TypeScript
export class StringUtil {
static getArriveMessage(): Resource {
return $r('app.string.message_arrive')
}
}
3.调用
按照之前的方式导包,然后调用工具类的方法即可
Text(StringUtil.getArriveMessage('可以拼接文本'))
TypeScript
import { add, Log, CommonText, StringUtil } from 'common'
import router from '@ohos.router'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
var result = add(1, 2)
this.message = "result=" + result
Log.info("----------------")
})
CommonText()
.onClick(() => {
router.pushUrl({ url: '@bundle:com.lf.myapplication/Common/ets/pages/CommonIndex' })
.then(() => {
console.log("push page success");
})
.catch(err => {
console.error(`pushUrl failed, code is ${err.code}, message is ${err.message}`);
})
})
Text(StringUtil.getArriveMessage('可以拼接文本'))
}
.width('100%')
}
.height('100%')
}
}
效果图:
五、跨包使用需要注意点:
- 要使用HSP中的接口,需要在使用方的oh-package.json5中配置对它的依赖
- 跨包使用的接口都需要添加export。对外暴露的接口,需要在入口文件index.ets中声明,同一个类的多个方法,可以写在一起,用,隔开
- 新增的共享包需要跨包调用,需要在Edit Configurations中添加新增的共享包