乐趣区

关于openharmony:我把分布式音乐播放器适配了Stage模型

OpenAtom OpenHarmony(以下简称“OpenHarmony”)利用开发自 API 8 及其更早版本始终应用的是 FA 模型进行开发。FA 模型是 Feature Ability 的缩写,它和 PA(Particle Ability)两种类型是过往长期推广的术语,深入人心。

然而从 API 9 开始,Ability 框架引入了 Stage 模型作为第二种利用框架状态,Stage 模型将 Ability 分为 PageAbility 和 ExtensionAbility 两大类,其中 ExtensionAbility 又被扩大为 ServiceExtensionAbility、FormExtensionAbility、DataShareExtensionAbility 等一系列 ExtensionAbility,以便满足更多的应用场景。新模型接口中有 AbilityStage/WindowStage 的概念,这个 Stage 自身有舞台的意思,寓意是给开发者一个新的展示舞台。Stage 模型的设计,次要是为了开发者更加不便地开发出分布式环境下的简单利用。下表给出了两种模型在设计上的差别:

能够看得出来,新的模型设计的次要指标是把 UI 与 Ability 拆散,即从架构设计层面,标准开发者编写业务逻辑和 UI 交互的开发方式。通过数据把 UI 和业务逻辑解耦,开发者在 Ability 中产生数据,数据传递给 UI 框架后,利用 ArkTS 申明式框架的特点,UI=F(state),通过数据驱动 UI 变动。这样的设计是为了更好地反对 Ability 实现跨端迁徙和多端协同,即数据都是存储在 Ability 里,继而通过数据驱动 UI 展现。此外,FA 模型每个 Ability 应用一个 VM 实例,而 Stage 模型整个过程只应用一个 VM 实例,缩小过程内存占用,利用内状态在过程内共享。

分布式音乐播放器,是往年上半年我基于 OpenHarmony 3.1,参考 OpenHarmony JS 分布式音乐播放的 Sample 代码,应用 ArkTS 新写的样例,过后的次要目标就是为了学习 ArkTS 开发页面。此次适配 Stage 模型后,在润和大禹系列 HH-SCDAYU200 开发套件上,成果如下图所示:

能够看到,此次更新,不仅应用了 Stage 模型适配,还应用 ArkTS 减少了一个音乐播放器首页列表的界面,以及播放时应用属性动画,实现了一个播放音乐时“唱片旋转”的动画成果。这次应用 Stage 模型适配样例,次要是批改了如下几个中央:

批改点 1:代码目录的调整

能够看到,绝对于 FA 的目录构造,首先是在最上层目录里,减少了一个 AppScope 目录,这个目录下也是 resources 下的资源文件,比方 string.json,图片等内容。这个目录里的资源文件,会在编译时拼接到具体的 hap 内编译,因而能够把不同 hap 包里的专用资源提取到这个目录下。

此外是减少了 AbilityStage.ts 这个文件,它是 Hap 及加载入口,开发者能够基于它派生实现 hap 的初始化以及指定多个实例开发。AbilityStage 能够配合 ApplicationContext 监听 / 治理过程内组件的生命周期,感觉是有点充当了 FA 模型里的 app.ets 的作用。

其它的文件也有小的变动,如配置文件,pages 地位等都有调整。所以倡议还是新建一个 stage 模型的工程,而后把之前的代码逐渐复制过去,而后批改问题。

批改点 2:获取设施列表,分布式拉起等 API 变动

因为两种模型的利用上下文不同,导致一些跟上下文相干的 API 大都有些变动,在 SDK 及文档中有明确表明哪些 API 是 stage 模型专用的。比方耳熟能详的 startAbility 分布式拉起利用,在 FA 模型中是通过以下代码实现:

import featureAbility from '@ohos.ability.featureAbility';

   featureAbility.startAbility({want: wantValue}).then((data) => {CommonLog.info('startAbilityContinuation finished,' + JSON.stringify(data))

      // 拉起后,自我敞开
      featureAbility.terminateSelf((error) => {CommonLog.info('startAbilityContinuation terminateSelf finished, error=' + JSON.stringify(error))
      })
    }).catch((error) => {CommonLog.info('startAbilityContinuation error' + JSON.stringify(error))
    })

而在 stage 模型里,因为不再有 featureAbility,因而无奈 import featureAbility,进而无奈应用 featureAbility.startAbility 拉起利用,进而应用 getContext 获取上下文后,调用 startAbility 拉起利用。

  getContext(this).startAbility(want).then((data) => {CommonLog.info('startAbilityContinuation finished,' + JSON.stringify(data))

      // 自我敞开
      getContext(this).terminateSelf((error) => {CommonLog.info('startAbilityContinuation terminateSelf finished, error=' + JSON.stringify(error))
      })
    }).catch((error) => {CommonLog.info('startAbilityContinuation error' + JSON.stringify(error))
    })

除了 startAbility 外,样例里应用到的获取蕴含 bundleName,设施发现 deviceManager 的相干 API 都须要依照上述办法进行批改。

批改点 3:数据从组件拆散,提取到 Ability 中

在分布式拉起时,须要传递以后播放的音乐和音乐的播放进度。在两种模型里,这些参数都是被设置在 wantValue 的 parameters 里,通过 startAbility 传出去。

let params = {index: this.playerManager.getCurrentMusicIndex(),
      seekTo: this.playerManager.getCurrentTimeMs(),
      isPlaying: this.isPlaying
    }
    let wantValue = {
      bundleName: this.bundleName,
      abilityName: 'com.madixin.music.MainAbility',
      deviceId: remoteDevice.deviceId,
      parameters: params
    }

但在接管参数时,FA 模型里,是在以后组件的代码里,通过 featureAbility.getWant 来获取参数,如下代码。

 featureAbility.getWant((error, want) => {CommonLog.info('restoreFromWant featureAbility.getWant=' + JSON.stringify(want))
      let status = want.parameters
      if (status != null && status.index != null) {this.playerManager.playSpecifyMusic(status.seekTo, status.index)
        this.isPlaying = true
        this.playAnimation()}
    })

而应用 Stage 模型后,尽管参数传递的形式是统一的,然而无奈间接在组件 UI 中获取参数,而须要先在 MainAbility.ts 获取参数 want。此时如果要传递给组件,有多种形式,这里我是应用的如下形式,即在 MainAbility.ts 的 onCreate 和 onNewWant 里,把 want 赋值到 globalThis 里,而后在 UI 组件里,通过 globalThis 获取参数。

 // MainAbility.ts
    onNewWant(want, launchParams) {
        globalThis.newWant = want
        hilog.info(0x0000, 'MyOpenHarmonyPlayer', '%{public}s', 'onNewWant launchParam:' + JSON.stringify(launchParams) ?? '');
    }

    onCreate(want, launchParam) {
        globalThis.newWant = want
        hilog.info(0x0000, 'MyOpenHarmonyPlayer', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
        hilog.info(0x0000, 'MyOpenHarmonyPlayer', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? '');
    }
    
    // index.ets
    let newWant = globalThis.newWant
    CommonLog.info("aboutToAppear newWant:" + JSON.stringify(newWant))
    if (newWant !== null && newWant.parameters.hasOwnProperty("seekTo")) {this.playerManager.playSpecifyMusic(newWant.parameters.seekTo, newWant.parameters.index)
    }

另外,理解到还有一种形式传递数据是应用 AppStorage 来关联,比方在 MainAbility.ts 里应用 AppStorage.SetOrCreate<string> 传入数据,在 UI 组件里,应用 @StorageLink 标签润饰变量来获取数据。

除以上三点批改外,还有两点值得阐明下

首先是因 OpenHarmony 3.2 后分布式能力限度智能零碎利用应用,须要晋升 apl 等级:找到所应用 API 版本对应 toolchains> 版本号 >lib>UnsgnedReleasedProfileTemplate.json,更改 “apl”: “normal” 为 “apl”: “system_core”。

其次是 API 9 当前辨别了 public-SDK 和 Full SDK。DevEco Studio 默认下载的是 public-SDK,它不蕴含零碎利用所须要的高权限 API。当咱们 import deviceManager from ‘@ohos.distributedHardware.deviceManager’ 时,会发现外面只有一个空的接口,没有任何办法。尽管这不影响性能,但代码中必须应用 @ts-ignore 疏忽 typescript 的告警,而且没有语法提醒。此时,须要应用 full-SDK 替换。

相干文档请参考 https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/quick-start/full-sdk-switch-guide.md
新增首页页面,和播放列表页的动画,不是本文的重点,大家能够参考代码自行学习。

总结

OpenHarmony 的 FA 模型能力曾经进行演进,后续将会加强 Stage 模型。此次将现有的样例代码适配 Stage 模型,尽管整体代码批改量不大,但因为惯性思维以及 API 的变动,期间还是踩了不少坑。我已在 OpenHarmony 常识体系仓中更新了样例代码,欢送开发者来参考和斧正问题,倡议新上手 OpenHarmony 的开发者能够间接学习应用新的 Stage 模型来开发利用。后面提到在 Stage 模型里,ExtensionAbility 又被扩大为 ServiceExtensionAbility、FormExtensionAbility、DataShareExtensionAbility 等一系列 ExtensionAbility,这个样例目前还没有波及到,待后续进一步学习,通过 ExtensionAbility 把音乐播放实现成一个后盾服务,从而实现利用在后盾时也能持续播放音乐,届时将继续更新这个利用,也欢送大家一起共建。

分布式音乐播放器样例地址 https://growing.openharmony.cn/mainPlay/detail?sampleId=3742

退出移动版