前言:
元服务(原为"原子化服务")是一种基于HarmonyOS API的全新服务提供方式,元服务仅需开发一次,支持多终端设备运行,以鸿蒙万能卡片等多种呈现形态,向用户提供更轻量化的服务。
鉴于明年鸿蒙系统可能不再兼容原生Android应用,本着事先做好准备的原则,我们决定先从鸿蒙的元服务入手,开发一个简易的元服务,然后再由多个元服务组建一个鸿蒙应用;
一、新建元服务项目
经过多年的发展,现在市面上较新的鸿蒙手机已经更新到4.0,开发文档为HarmonyOS 3.1/4.0,本着用新不用旧的原则,下面项目我们以ArkTS开发项目;
开发工具:DevEco studio 3.1.1Relesae 开发环境:Mac m1
真机调试:华为mate40pro 系统:鸿蒙4.0.0.122
新建项目:Create Project > Atomic Service >Empty Ability;Model选择Stage;Language:ArkTS
二、项目简介
csharp
├──ets #存放ArkTS源码
│ ├── common #公共方法
│ ├── components #公共组件
│ ├── entryability #应用/元服务入口
│ ├── entryformability #卡片入口
│ ├── model #模型
│ ├── pages #页面
│ ├── widget #存放卡片源码
├──resources #存放应用/元服务所用到的资源文件
│ ├── base
│ │ ├── element
│ │ ├── media
│ │ └── profile
│ │ ├── form_config.json #卡片配置文件
│ │ └── main_pages.json #主工程页面配置
│ ├── en_US
│ ├── rawfile
│ ├── zh_CN
│ └── module.json5 #模块配置文件
三、基础功能实现
我们以对话框的形式实现一个简单的诊股功能;
一、UI模块:下面对UI部分做些简单的分析
首先我们需要一个输入模块放在底部;我们在左边放置一个发现的引导按钮,中间文本输入,右边发送按钮;
kotlin
Flex({direction:FlexDirection.Row, wrap:FlexWrap.NoWrap}) {
Button({stateEffect:false}) {}
.onClick(() => {
this.isShowDiscovery = true;
})
TextInput({placeholder:'有问题尽管问我~',text:this.question })
.onChange((value: string) => {
this.question = value;
this.searchD(value);
})
Button({stateEffect:false}) {}
.onClick(() => {
this.requestV3(this.question);
this.focusForce();
})
}
二、根据需求对问题及内容进行展示
中间是内容展示区域,针对列表插入数据刷新的性能问题,采用懒加载列表内容的方式来减少相关性能损耗;
scss
LazyForEach(this.data, (item: ChatQaInfo) => {
ListItem() {
if (item.type=='stockList') {
this.createStockList(item);
} else if (item.type=='summary') {
this.createSummary(item);
} else {
Row(){
Text(item.content)
.textAlign(item.type=='ask'?TextAlign.End:TextAlign.Start)
}.width('100%')
.justifyContent(item.type=='ask'?FlexAlign.End:FlexAlign.Start)
}
}
})
三、多设备适配
为了适配多设备,整个页面我们使用GridRow/GridCol组件来管理UI展示,具体结构如下:
php
@State private isBigScreen: boolean = false;
....
GridRow({
breakpoints: {
value: ['320vp', '520vp', '840vp', '1080vp'],
reference: BreakpointsReference.WindowSize,
},
direction:GridRowDirection.Row,
columns:{
xs:3,
sm:3,
md:5,
lg:5,
xl:5,
}
}){
// 大屏左侧新增栏目
if (this.isBigScreen) {GridCol({span:2}) {......}}
GridCol({span:this.isBigScreen ? 3: 3}){
// 小屏UI
......
}.onBreakpointChange((breakpoints: string) => {
// 监听断点变化
this.isBigScreen = (breakpoints == 'md' || breakpoints == 'lg' || breakpoints == 'xl');
})
四、网络请求
网络请求我们需要对ohos.net.http库进行简单的封装; 首先我们需要添加权限,在module.json5中添加 "requestPermissions":[{"name": "ohos.permission.INTERNET"}]来开启网络权限 然后我们新建一个Http,设置网络请求需要的属性:
kotlin
import http from '@ohos.net.http'
class Http {
url: string
header: Object
extraData: Object
options: http.HttpRequestOptions
constructor() {
this.url = ''
this.options = {
method: http.RequestMethod.GET,
header: this.header,
extraData: this.extraData,
readTimeout: 50000,
connectTimeout: 50000,
expectDataType: http.HttpDataType.STRING, // 可选,指定返回数据的类型
}
}
setUrl(url: string) {
this.url = url
}
setHeader(header: Object) {
this.header = header
this.options.header = this.header;
}
setMethod(method: string) {
switch (method) {
case 'GET':
this.options.method = http.RequestMethod.GET;
break ...
}
}
setExtraData(extraData: Object) {
this.extraData = extraData
this.options.extraData = this.extraData;
}
完成这些后我们使用http库实现网络请求:
javascript
requestPromise() {
return new Promise((resolve, reject) => {
let httpRequest = http.createHttp();
let response = httpRequest.request(
this.url,
this.options,
);
response.then((data) => {
if (data.responseCode == 200) {
// 处理返回结果
const response = data.result + "";
const res = JSON.parse(response);
resolve(res.data)
} else {
// todo 请求失败,进行失败逻辑处理
reject(data)
}
}).catch((err) => {
// todo 请求失败,进行失败逻辑处理
console.info('error==>' + JSON.stringify(err));
reject(err)
})
})
}
}
使用时直接导入Http,完成相应的设置即可
scss
Http.setUrl(url)
Http.setMethod(method)
Http.setHeader(header)
Http.setExtraData(params)
Http.requestPromise().then((res)=>{
//请求成功后逻辑
options.success(res);
}).catch((error)=>{
//请求失败后逻辑
options.fail(error);
})
四、卡片
一、简介
我们在第一张效果图上能看到右上角有个圆形图标;在元服务上线后,我们点击该图标就会弹出添加到桌面的菜单;但是在调试阶段,只能通过双指压缩>服务卡片>其他服务卡片>你的元服务>添加到桌面/添加到负一屏;我们的卡片效果图如下:
二、定时、定点刷新
卡片我们主要关注3个文件:form_config.json 卡片配置;WidgetCard.ets 卡片的UI设置;EntryFormAbility.ts 卡片入口文件,卡片逻辑处理都在这; 我们先看form_config.json
json
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
// 开启自动刷新
"updateEnabled": true,
// 定点刷新时间(24小时制),定点刷新一次
"scheduledUpdateTime": "15:00",
// 定时刷新时间每updateDuration * 30分钟刷新一次;
// 该值为不为0时,会覆盖定点刷新;
// 为0时触发定点刷新;
// 如我们可以将updateDuration设置为1,每30秒刷新一次大盘解析内容;
"updateDuration": 0,
"defaultDimension": "2*4",
"supportDimensions": [
"2*4"
]
}
]
我们看看设置项:"updateEnabled": true,//开启自动刷新;"scheduledUpdateTime": "12:54",//定点刷新时间(24小时制);固定时间定点刷新一次;"updateDuration": 1,//定时刷新时间每updateDuration*30分钟刷新一次;该值为不为0时,会覆盖定点刷新;为0时触发定点刷新;
触发刷新后会执行EntryFormAbility的onUpdateForm方法;如果不想持续刷新,可以不写setFormNextRefreshTime方法,直接执行updateForm方法;
javascript
onUpdateForm(formId) {
try {
formProvider.setFormNextRefreshTime(formId, 5, (err, data) => {
if (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
return;
} else {
let currentDate = new Date();
// 获取当前分钟
let minutes = currentDate.getMinutes().toString();
let timeStr = '当前时间:'+ minutes +'分'
let formData = {
'title': '测试定时刷新', // 和卡片布局中对应
'detail': timeStr, // 和卡片布局中对应
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
}
});
} catch (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
}
}
在创建卡片时,会执行EntryFormAbility里面的onAddForm(want)方法,我们可以从want中获取formId,保存下来,以供后面主工程使用;当前其他方法里面有formId也可以保存;
let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY]; PreferencesUtil.createDataPreferences(this.context); PreferencesUtil.saveDataPreferences("formId",formId); 如我们点击了卡片上的实时刷新功能;我们就可以获取保存过formId,在主工程调用 formProvider.updateForm方法来刷新卡片;
三、手动刷新
一般来说,卡片上的内容都是定时、定点刷新,或者根据主工程的需要刷新,也可以在不打开元服务的情况下,直接在卡片上刷新内容; 在云服务打开的情况下,我们使用元服务中的数据formProvider.updateForm方法来刷新卡片; 仅在元服务关闭的情况下,卡片才可以直接调用网络请求去刷新数据;
五、小结
华为鸿蒙正处于全新的发展阶段,它的开放性和创新性也意味着更多的机会和挑战。正如古语所说,"理无专在,学无止境",未来鸿蒙势必会不断推出更多功能,同时,我们也需要不断学习和探索新的技术和趋势,以保持竞争力和创新力。期待NEXT版本全面开放。