在学习元服务之前先简单介绍一下元服务是什么
元服务(原名为原子化服务)是HarmonyOS提供的一种面向未来的服务提供方式,是有独立入口、免安装、可为用户提供一个或多个便捷服务的新型应用程序形态。 元服务基于HarmonyOS APY开发,支持运行在148+N设备上,供用户在合适的场景、合适的设备上便捷使用。元服务相对于传统方式的需要安装的应用形态更加轻量,同时提供更丰富的入口、更精准的分发。(来自官方文档)
注:简单点说,有点像现在版本的微信小程序。原理上来说只要改变一个属性就可以将一个应用转为一个元服务
左边这个是新版手机的负一层,就是元服务的启动位置
右侧就是元服务的服务市场
元服务,传统应用,服务卡片之间的区别与关系:
(图片来自官方开发文档)
从图上看,一个元服务它有一个首包,和若干分包和依赖库,统一上传到应用市场上后,由应用市场去分发,进入负一层的页面(这里就引出一个元服务的一个重要的知识,就是分包)
分包:
官方给的分包建议是:
1》一个加上所有依赖的分享包超过2M就需要分包
2》 分包的时候按功能分
3》 重复代码分HSP
关于元服务的分包规格
首包:将Enty HAP作为首包,包含元服务首次启动时会打开的页面(即首页)代码和资源。
分包:将其他包含功能页的模块以及HSP动态共享模块作为分包,而HSP包在使用的时候是作为引用的方式来使用,其中包含功能页和元服务依赖的代码和资源。
单个包文件(加上其依赖的所有共享包),大小不能超过2MB,超过限制DevEco Studio会打包失败。
同一个元服务下所有包文件(加上其依赖的所有共享包)的大小总和不能超过10MB,超过限制DevEco Studio会打包失败。
所以在启动元服务时,只需下载和安装首包,即可立即启动元服务,大大缩短元服务启动时间。
注:如果有那么多包的情况下,应用开发和元服务开发给我的感觉其实没有体验出有啥特别的区别,就是在使用元服务开发的情况下,会在开发的index中有额外的代码:
/*************额外代码***************** * 使用华为账号登录 * * 此方法通过调用认证控制器执行华为账号登录请求 * 它首先创建一个带有华为ID的登录请求配置,然后使用认证控制器执行该请求 * 成功登录后,它将处理返回的数据,特别是授权码 * 如果登录过程中出现错误,它将捕获并处理特定的错误,例如账号未登录错误 */ private loginWithHuaweiID() { // 创建带有华为ID的登录请求,并设置是否强制登录的标志 let loginRequest = new authentication.HuaweiIDProvider() .createLoginWithHuaweiIDRequest(); loginRequest.forceLogin = false; // 初始化认证控制器 let controller = new authentication.AuthenticationController(); // 执行登录请求,并处理返回的数据 controller.executeRequest(loginRequest).then((data) => { // 将返回的数据转换为登录响应类型,并提取授权码 let loginWithHuaweiIDResponse = data as authentication.LoginWithHuaweiIDResponse; let authCode = loginWithHuaweiIDResponse.data?.authorizationCode; // 这里可以添加处理授权码的逻辑 }).catch((error: BusinessError) => { // 记录错误日志 hilog.error(0x0000, 'testTag', 'error: %{public}s', JSON.stringify(error)); // 处理特定的认证错误,例如账号未登录 if (error.code == authentication.AuthenticationErrorCode .ACCOUNT_NOT_LOGGED_IN) { // 这里可以添加处理账号未登录错误的逻辑 } }); }
代码解释
这段代码实现了通过华为ID进行登录的功能。具体步骤如下:
- 创建登录请求并设置参数。
- 设置是否强制显示华为ID登录页面。
- 执行登录请求,获取响应数据。
- 从响应数据中提取授权码,并发送到后端以换取unionID和会话信息。
- 捕获可能的错误,记录错误日志,并根据错误类型进行相应处理。
详细解释
- 创建登录请求:使用
authentication.HuaweiIDProvider
创建一个登录请求对象loginRequest
。- 设置强制登录参数:设置
loginRequest.forceLogin
为false
,表示不强制显示华为ID登录页面。- 执行登录请求:使用
authentication.AuthenticationController
的executeRequest
方法执行登录请求。- 获取授权码:从登录响应数据中提取
authorizationCode
。- 发送授权码到后端:将提取的授权码发送到后端,以换取
unionID
和会话信息。- 捕获错误:如果登录请求失败,捕获错误并记录日志。
- 记录错误日志:使用
hilog.error
记录错误信息。- 错误类型判断:根据错误类型进行处理,如果是账号未登录,则建议跳转到登录引导页;其他错误则结束流程
分包涉及到跨模块页面路由
跨模块路由使用的也是Navigation 和NavDestination作为目标还用到到了NavPushPathHelper但是基本的操作是一样的
- 首先要有两个模块分别是:一个entry一个library
假设现在A跳转到B索要准备的代码
-
在A的跳转页面要写跳转请求
import { NavPushPathHelper } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit';
// 定义一个入口组件Index
@Entry
@Component
struct Index {
// 定义一个状态变量message
@State message: string = 'Hello World';
// 初始化一个导航路径栈
pathStack: NavPathStack = new NavPathStack()
// 以NavPathStack的示例为参数生成NavPushPathHelper示例
helper: NavPushPathHelper = new NavPushPathHelper(this.pathStack)
// 组件构建方法
build() {
// 使用Navigation组件并传入路径栈
Navigation(this.pathStack){
// 创建一个列布局
Column() {
// 创建一个按钮,点击时跳转到ProductList页面
Button("跳转到ProductList")
.onClick(()=>{
console.log("跳转到ProductList")
// 使用helper的pushPath方法进行页面跳转
this.helper.pushPath('library', { name: 'ProductList' }, false)
.then(() => {
console.error('[pushPath]success.');
})
.catch((error: BusinessError) => {
console.error(`[pushPath]failed, error code = ${error.code}, error.message = ${error.message}.`);
});
})
}
.width('100%')
}.title('NavIndex')
.height('100%')
}
}
主包就基本上不用写了剩下的就是别的包了—这里我只写了一个页面,也就是说跳转一次.
这个页面是跳转到的页面
// 使用Builder装饰器标记ProductListBuilder函数,使其能够使用构建者模式
@Builder
// ProductListBuilder函数,目的是构建产品列表组件
export function ProductListBuilder() {
ProductList()
}
// 使用Entry和Component装饰器标记ProductList结构体,Entry表示这是一个入口组件,Component表示这是一个可复用的组件
@Entry
@Component
// 定义一个名为ProductList的组件结构体,用于展示产品列表
export struct ProductList {
// 定义一个导航路径栈属性,用于管理导航路径
pathStack: NavPathStack = new NavPathStack()
// 构建方法,用于渲染组件
build() {
// 创建一个导航目的地,并在其中添加一个文本元素
NavDestination() {
Text("跳转程序成功")
}
// 设置导航目的地的标题
.title('产品列表')
// 在导航目的地准备就绪时执行的回调函数
.onReady((context: NavDestinationContext) => {
// 将当前导航路径栈赋值为导航目的地上下文中的路径栈
this.pathStack = context.pathStack
})
}
}
还有就是两个配置上的内容,基本上和单模块页面路由一样了
{
"routerMap": [
{
"name": "ProductList",
"pageSourceFile": "src/main/ets/pages/ProductList.ets",
"buildFunction": "ProductListBuilder",
"data": {
"description" : "this is ProductList"
}
}
]}
{
//这个是子包的module.json5
"module": {
"name": "library",
"type": "shared",
"description": "$string:shared_desc",
"deviceTypes": [
"phone",
"tablet"
],
"routerMap": "$profile:route_map",
"deliveryWithInstall": true,
"installationFree": true,
"pages": "$profile:main_pages"
}
}
注(这个非常重要是官方文档的原话一定要记得设置): 主包没有直接依赖要 跳 转的 目 标页 面 所在 的分包,运行/调试的时候DevEco Studio不会自动把跳转的目标页面所在分包也安装到手机上,需要单击“Run > Edit Configurations > entry > Deploy Multi Hap”,勾选“Deploy Multi Hap Packages”并选择加载目标页面所在的分包。
做的这里的话你的分包就完成了,基本上的一些小的元服务的分包就可以做了,当然这只是一个demo。到了正常开发的时候肯定会各种页面套着跳转,这就需要去看具体的开发要求了。
预加载
定义:(这是一个概念性的问题)通过配置预加载,由系统自动下载和安装可能需要的分包模块,从而提升进入后续模块的速度。
预加载在相应分包模块module.json5配置文件中“atomicService”标签下的preloads字段配置。以entry模块的module.json5为例:
{
"module": {
"name": "entry",
"type": "entry",
"installationFree": true,
"pages": "$profile:main_pages",
"atomicService": {
"preloads": [
{
"moduleName": "library"
}
]
},
...
}
}
代码解释
这段JSON5代码定义了一个名为 atomicService
的服务配置,其中包含一个 preloads
数组。数组中有一个对象,指定了一个模块名为 library
的预加载模块。
注:看着哼虎人:简单来说就是,当A渲染完成后需要渲染B的时候在A的ability中写上面的代码,来完成预加载,保证用户端的体验和流畅
可以结合git地址上的实例代码一起配合看:
鸿蒙_元服务: 鸿蒙初学者学习手册_元服务(一)元服务(原名为原子化服务)是HarmonyOS提供的一种面向未来的服务提供方式,是有独立入口、免安装、可为用户提供一个或多个便捷服务的新型应用程序形态。