鸿蒙HarmonyOS元服务项目初探

前言:

元服务(原为"原子化服务")是一种基于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版本全面开放。

相关推荐
张帅涛_66644 分钟前
HarmonyOS ArkUI 构建布局
华为·harmonyos
冯志浩10 小时前
Harmony NEXT:如何给数据库添加自定义分词
harmonyos·掘金·金石计划
爱桥代码的程序媛12 小时前
鸿蒙OpenHarmony【轻量系统芯片移植案例】标准系统方案之瑞芯微RK3568移植案例
嵌入式硬件·harmonyos·鸿蒙·鸿蒙系统·移植·openharmony·鸿蒙开发
AORO_BEIDOU13 小时前
防爆手机+鸿蒙系统,遨游通讯筑牢工业安全基石
5g·安全·智能手机·信息与通信·harmonyos
小强在此1 天前
【基于开源鸿蒙(OpenHarmony)的智慧农业综合应用系统】
华为·开源·团队开发·智慧农业·harmonyos·开源鸿蒙
PlumCarefree1 天前
基于鸿蒙API10的RTSP播放器(四:沉浸式播放窗口)
华为·harmonyos
中关村科金1 天前
中关村科金推出得助音视频鸿蒙SDK,助力金融业务系统鸿蒙化提速
华为·音视频·harmonyos
小强在此2 天前
基于OpenHarmony(开源鸿蒙)的智慧医疗综合应用系统
华为·开源·团队开发·健康医疗·harmonyos·开源鸿蒙
奔跑的露西ly2 天前
【鸿蒙 HarmonyOS NEXT】popup弹窗
华为·harmonyos