OpenHarmony开发者论坛

标题: 桌面卡片开发教程03:从底层原理开始讲透桌面卡片的router事件刷新机制【坚果派-咸鱼】 [打印本页]

作者: 翻身咸鱼    时间: 2024-1-29 08:54
标题: 桌面卡片开发教程03:从底层原理开始讲透桌面卡片的router事件刷新机制【坚果派-咸鱼】
[md]> **坚果派由坚果创建,团队拥有 12 个华为 HDE,以及若干其他领域的三十余位万粉博主运营。
> 团队成员聚集在北京,上海,南京,深圳,广州,宁夏等地,欢迎合作。**

**首先铺垫两个基础知识:**

**1.为什么桌面卡片需要使用特殊机制来刷新?**

* 主要有两个原因:第一是OpenHarmonyOS Api9的桌面卡片出于降低系统能耗的目的,被限制了只有5秒的活动时间。超过5秒以后桌面卡片的相关进程会被强制销毁,变成一个静态的页面。只有通过router机制、call机制或者message机制拉起相关后台,才能再次进行卡片内容的刷新。
* 第二个原因是从实际的运行机制来说,桌面卡片实际上并不是应用主体的一部分,而是归属于OpenHarmony系统下的桌面应用所管理的一系列服务,桌面卡片与其对应的应用主体之间相互隔离,只能使用专门的接口来进行数据交互与页面管理。
* 每张卡片都有一个独立的LocalStorage可以用来存储页面级变量,但同一个LocalStorage的数据只能在UIAbility内部共享,对外隔离,UIAbility无法直接访问。
* **桌面卡片刷新机制的本质就是通过专门的接口,改变特定桌面卡片的LocalStorage参数。以实现桌面卡片的UI更新。**

**2.router机制、call机制与message机制有什么不同?**

* 这三个机制都可以用来刷新桌面卡片的,三种机制的数据都以JSON的格式进行配置,并使用formBindingData.createFormBindingData()函数构建数据对象。
* 主要区别在于:
* **router机制**会直接打开应用界面,效果有点像点击桌面图标。也可以带参数打开应用,直接进入应用内部的某个特定位置,或者触发某项功能。更适用于桌面卡片结构复杂或是需要用户参与自定义卡片内容的场景。
* **call机制**是不打开应用界面,仅在后台拉起应用主体的UIAbility,来执行UIAbility内部的相关代码。call机制不受5秒时长的限制,可以先实现复杂且费时的数据加载,再提供给桌面卡片进行刷新。
* **message机制**则不涉及到应用的UIAbility,只是拉起桌面卡片自己的FormAbility,也可以刷新卡片,但仍然受5秒时长的限制,更适合轻量化的的实现卡片内容的刷新。

---

**接下来进入正式讲解:**(本案例使用router机制,拉起FormAbility来刷新卡片内容。)

其实**router机制**最主要的用处还是从卡片跳转应用主体,有点类似于点击桌面图标的效果,但是因为**router可以带参启跳转**,能实现直接进入特定页面或者启动特定功能的效果。

router实现卡片刷新效果的机制本质上就是在跳转后由程序自动或是用户手动执行updateForm接口,以刷新卡片内容。

**使用router机制刷新卡片的全流程主要分为3个阶段:**

1. (用户点击时)通过卡片的**postCardAction接口**触发router事件,将需要的信息作为参数提交,由系统启动对应应用的UIAbility→
2. UIAbility启动成功,**onCreate**或**onNewWant生命周期**获取到对应的FormID与入参,根据入参直接进行数据准备后通过**updateForm接口**对指定卡片执行刷新事件/或根据入参启动特定页面,在page页面进行数据准备后通过**updateForm接口**对指定卡片执行刷新事件→
3. 卡片page页面接收到数据,更新卡片界面。

**由于router机制发送给UIAbility的want中已经包含了卡片信息,因此可以不需要手动传递FormID,直接使用FormInfo接口获取formId。**

---

**案例效果展示:**

[鸿蒙元服务卡片router刷新效果](https://www.bilibili.com/video/BV1jh4y1A7vF/)

**案例代码:**

1. **(用户点击时)通过卡片的postCardAction接口触发router事件,将需要的信息作为参数提交,由系统启动对应应用的UIAbility→**

【卡片page的代码】

```plain
let storage=new LocalStorage()
@Entry(storage)
@Component
struct WidgetCard {

  @LocalStorageProp('textInfo') textInfo:string='原始文本内容'

  build() {
      Column() {
          Text(this.textInfo)
            .fontColor(Color.Black)
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)
      .onClick(() => {
      postCardAction(this, {
        "action": "router",
        "abilityName": 'EntryAbility',
        "params": {
          "detail": ''
        }
      });
    })
  }
}
```

2. **UIAbility启动成功,onCreate或onNewWant生命周期获取到对应的FormID与入参,根据入参直接进行数据准备后通过updateForm接口对指定卡片执行刷新事件/或根据入参启动特定页面,在page页面进行数据准备后通过updateForm接口对指定卡片执行刷新事件→**

**【Router后自动刷新的方案】**

UIAbility的关键代码:

```plain
export default class EntryAbility extends UIAbility {
  onCreate() {
  if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
      let FormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];//获取formID
      //let message = JSON.parse(want.parameters.params).detail;//读取入参
      let formData = {
        "textInfo": '卡片内容已更新', // 和卡片布局中对应
      };
      let formMsg = formBindingData.createFormBindingData(formData)
      formProvider.updateForm(FormId, formMsg).then((data) => {
        console.info('updateForm success.' + JSON.stringify(data));
      }).catch((error) => {
        console.error('updateForm failed:' + JSON.stringify(error));
      })
    }  
  }
  onNewWant(want){
    if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
      let FormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];//获取formID
      //let message = JSON.parse(want.parameters.params).detail;//读取入参
      let formData = {
        "textInfo": '卡片内容已更新', // 和卡片布局中对应
      };
      let formMsg = formBindingData.createFormBindingData(formData)
      formProvider.updateForm(FormId, formMsg).then((data) => {
        console.info('updateForm success.' + JSON.stringify(data));
      }).catch((error) => {
        console.error('updateForm failed:' + JSON.stringify(error));
      })
    }
  }
}
```

**【Router后用户手动刷新的方案】**

UIAbility的关键代码:

```plain
export default class EntryAbility extends UIAbility {
  onNewWant(want){
    if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
      let FormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
      //let message = JSON.parse(want.parameters.params).detail;
      AppStorage.SetOrCreate('formId',FormId)//保存当前卡片的FormID
    }
  }
}
```

Page页的关键代码:

```plain
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';

@Entry
@Component
struct  homepage {
  @State cardText: string = ''
  build() {
    Column(){
      TextInput()
        .onChange((res)=>{
          this.cardText=res
        })
      Button('刷新卡片')
        .onClick(async () => {
          if (AppStorage.Has('formId')) {
            let formId: string = AppStorage.Get('formId')//获取UIAbility保存的FormID
            let formData = {
              "textInfo": this.cardText
            };
            let formMsg = formBindingData.createFormBindingData(formData)
            try {
              await formProvider.updateForm(formId, formMsg)//刷新卡片
              AlertDialog.show({
                message:'卡片刷新成功'
              })
            }
            catch (error) {//失败回调
              AlertDialog.show({
                message:'卡片刷新失败,错误信息:'+JSON.stringify(error)
              })
            }
          } else {
            AlertDialog.show({
              message: '请先添加卡片'
            })
          }
        })
    }
    .width('100%')
    .height('100%')
  }
}
```

3. **卡片page页面接收到数据,更新卡片界面。**

【即1.的代码,updateForm成功后会根据LocalStorage中特定参数的变化会自动刷新】

```plain
let storage=new LocalStorage()
@Entry(storage)
@Component
struct WidgetCard {

  @LocalStorageProp('textInfo') textInfo:string='原始文本内容'//localStorage变化后自动同步,更新UI

  build() {
      Column() {
          Text(this.textInfo)
            .fontColor(Color.Black)
      }
      .width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)
      .onClick(() => {
      postCardAction(this, {
        "action": "router",
        "abilityName": 'EntryAbility',
        "params": {
          "detail": ''
        }
      });
    })
  }
}
```

---

postCardAction接口router事件的写法如下:

![router写法.png](https://forums-obs.openharmony.c ... yr099j3yc9z8saa.png "router写法.png")

![action参数.png](https://forums-obs.openharmony.c ... amzrdqrr2l4q2bz.png "action参数.png")

updateForm接口的写法如下:

![router响应端.png](https://forums-obs.openharmony.c ... qz1xyxtqxolk199.png "router响应端.png")
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5