继上次数据模块“搬家”成功后,咱哏儿都队又一头扎进了视频应用的升级大坑——毕竟“一次开发,多端部署”可是开源鸿蒙的拿手好戏,而视频应用作为多端场景的“刚需选手”,从API9的ohos前缀到API20的kit前缀迁移,简直是一场“全家桶设备适配大考”。咱依旧抱着润和RK3568(Dayu200同款芯,大赛指定“战板”),靠着AtomGit老师和小助手的神仙指导,边踩坑边通关,把这场多端适配的硬仗,唠给各位参赛战友听~

先交代背景:本次升级的主角,是一款基于ArkTS开发的视频应用,主打“一次开发,多端部署”——手机、折叠屏、平板(RK3568开发板模拟大屏场景)都能跑,API9时代靠着ohos.mediaquery、ohos.ui等接口勉强撑场面,但多端适配的“bug暴击”简直防不胜防。如今要升级到OpenHarmony 6.0(API20),核心就是把所有ohos前缀的接口,统一迁移到kit.ArkUI、kit.Media等新接口,同时保住多端部署的核心能力,还要在RK3568上跑出流畅效果,毕竟大赛评分里,多端适配和运行稳定性可是重头戏。

一、API9时代:多端适配的“潦草凑活版”,bug比视频弹幕还多

在API9时代,这款视频应用的“多端部署”,说好听点是“兼容多设备”,说难听点就是“一刀切+凑活修”。咱当时靠着ohos.mediaquery做断点判断,用ohos.ui的基础组件拼界面,在RK3568上跑起来,简直是“大型翻车现场”,总结下来就是三大痛点,每一个都能让参赛选手挠秃头:

痛点一:接口散乱,多端适配“各自为战”。API9里,媒体查询、栅格布局、组件渲染的接口分散在不同的ohos子模块里,比如媒体查询要引@ohos.mediaquery,栅格布局要靠ohos.ui的Grid组件,视频播放又要调用ohos.multimedia.media的接口。写代码时,光是记接口路径就够费劲儿,更别说多端适配时,要在不同设备的判断逻辑里,切换不同的接口调用方式——比如手机端和RK3568大屏端的轮播图显示数量,要写两套完全独立的判断逻辑,冗余代码多到离谱,AtomGit老师第一次检查我们代码时,直接吐槽“你们这代码,多端适配不是适配,是重复造轮子”。

痛点二:断点判断“迟钝”,多端切换“卡成PPT”。靠着ohos.mediaquery做设备尺寸判断,响应速度慢得惊人,在RK3568上切换模拟大屏和小屏模式,要等1-2秒才能刷新界面布局,有时候甚至直接卡死,视频播放和界面布局脱节,比如视频已经切换到全屏,侧边栏还停留在小屏状态。更坑的是,不同设备的断点阈值不统一,手机端显示正常的布局,放到RK3568上,要么控件挤成一团,要么留白多到浪费屏幕,多端的“美观度”根本无从谈起。

痛点三:组件兼容性差,RK3568算力“被浪费”。API9的ohos组件功能简陋,而且兼容性极差,比如我们用ohos.ui的List组件做视频列表,在手机端跑起来还算流畅,但放到RK3568上,加载10条以上视频数据,就开始卡顿、掉帧,滑动时还会出现组件错位的情况。要知道,RK3568的RK3568芯片算力不算弱,却被API9的老旧组件限制住了,相当于“给跑车装了个自行车轮胎”,AtomGit的小助手提醒我们,这是因为ohos组件没有针对多端设备做优化,无法充分调用开发板的硬件资源。

最致命的是,API9的这些接口,根本不支持Stage模型,也无法适配OpenHarmony 6.0的新特性,要参加本次升级适配大赛,必须彻底抛弃这些老旧的ohos接口,全面迁移到kit前缀的新接口,这也是我们本次升级的核心任务。

二、API20登场:kit前缀“大一统”,多端适配“丝滑到起飞”

如果说API9的ohos接口是“散乱的零件”,那API20的kit前缀接口,就是“组装好的精密机器”。OpenHarmony 6.0对接口进行了全面重构,将原来分散的ohos子模块,统一整合到kit命名空间下,比如媒体查询归入@kit.ArkUI,视频播放归入@kit.Media,栅格布局直接集成在ArkUI的组件中,实现了“接口统一、能力升级、多端适配更高效”,简直是为我们参赛选手量身定制的“福音”,咱在RK3568上实测后,只能说“早用早香”!

升级亮点一:接口“大一统”,多端适配“少走弯路”。API20里,所有和界面、交互、媒体相关的接口,都统一归入kit前缀,比如原来的@ohos.mediaquery,直接替换成@kit.ArkUI的mediaquery;原来的ohos.ui组件,全部替换成@kit.ArkUI的ArkTS组件;视频播放的接口,从@ohos.multimedia.media,迁移到@kit.Media。这样一来,写代码时,只需要引入对应的kit模块,就能调用所有需要的接口,再也不用记繁琐的ohos子模块路径,冗余代码直接砍半。更重要的是,多端适配时,所有设备的判断逻辑、组件渲染方式都统一了,比如手机端和RK3568大屏端的轮播图显示数量,只需要通过一套断点逻辑、一套栅格配置就能实现,AtomGit老师看了我们迁移后的代码,夸我们“总算get到了多端适配的精髓”。

引入与使用流程

媒体查询通过mediaquery模块接口,设置查询条件并绑定回调函数,任一媒体特征改变时,均会触发回调函数,返回匹配结果,根据返回值更改页面布局或者实现业务逻辑,实现页面的响应式设计。具体步骤如下:

首先导入媒体查询模块。

import { mediaquery } from '@kit.ArkUI';

大家可以从鸿蒙官网里找到对应的文档。

媒体查询 (@ohos.mediaquery)https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-layout-development-media-query

升级亮点二:mediaquery升级,断点响应“毫秒级”。API20的@kit.ArkUI.mediaquery,相比原来的ohos.mediaquery,响应速度提升了不止一个档次,在RK3568上切换模拟多设备模式,界面布局瞬间刷新,没有丝毫延迟。而且,断点阈值可以自定义配置,还支持根据设备类型、屏幕方向、窗口宽高等多种维度做判断,比如我们可以专门为RK3568设置专属的断点阈值,让大屏布局更合理,充分利用RK3568的屏幕空间,再也不会出现“控件拥挤”或“留白过多”的问题。

这也就是我们说的

媒体特征(media-feature)

媒体特征包括应用显示区域的宽高、设备分辨率以及设备的宽高等属性。

示例一使用媒体查询,实现屏幕横竖屏切换时,为页面文本应用添加不同的内容和样式。

Stage模型下的示例:

import { mediaquery } from '@kit.ArkUI';
import { window } from '@kit.ArkUI';
import { common } from '@kit.AbilityKit';

@Entry
@Component
struct MediaQueryExample {
  @State color: string = '#DB7093';
  @State text: string = 'Portrait';
  // 当设备横屏时条件成立
  listener:mediaquery.MediaQueryListener = this.getUIContext().getMediaQuery().matchMediaSync('(orientation: landscape)');

  // 当满足媒体查询条件时,触发回调
  onPortrait(mediaQueryResult:mediaquery.MediaQueryResult) {
    if (mediaQueryResult.matches as boolean) { // 若设备为横屏状态,更改相应的页面布局
      this.color = '#FFD700';
      this.text = 'Landscape';
    } else {
      this.color = '#DB7093';
      this.text = 'Portrait';
    }
  }

  aboutToAppear() {
    // 绑定当前应用实例
    // 绑定回调函数
    this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
      this.onPortrait(mediaQueryResult);
    });
  }

  aboutToDisappear() {
    // 解绑listener中注册的回调函数
    this.listener.off('change');
  }

  // 改变设备横竖屏状态函数
  private changeOrientation(isLandscape: boolean) {
    // 获取UIAbility实例的上下文信息
    let context:common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
    // 调用该接口手动改变设备横竖屏状态
    window.getLastWindow(context).then((lastWindow) => {
      lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT);
    });
  }

  build() {
    Column({ space: 50 }) {
      Text(this.text).fontSize(50).fontColor(this.color);
      Text('Landscape').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
        .onClick(() => {
          this.changeOrientation(true);
        });
      Text('Portrait').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
        .onClick(() => {
          this.changeOrientation(false);
        });
    }
    .width('100%').height('100%')
  }
}

    图1 竖屏

    图2 横屏

    升级亮点三:组件能力拉满,RK3568算力“被激活”。API20的@kit.ArkUI组件,在功能和性能上都进行了全方位升级,不仅支持更丰富的自适应布局和响应式布局能力,还针对不同设备的硬件特性做了优化。比如我们用@kit.ArkUI的GridRow、GridCol组件做栅格布局,在RK3568上加载视频列表,即使加载20条以上数据,也能流畅滑动,没有丝毫卡顿;视频播放用@kit.Media的接口,支持硬解码,在RK3568上播放高清视频,画面流畅、声音清晰,再也不会出现“音画不同步”的问题,真正发挥出了RK3568芯片的算力优势。

    示例二使用媒体查询实现屏幕横竖屏切换时Flex组件的不同布局,竖屏时Flex采用垂直方向布局,横屏时采用水平方向布局。

    import { LengthMetrics, mediaquery, window } from '@kit.ArkUI';
    import { common } from '@kit.AbilityKit';
    
    @Entry
    @Component
    struct MediaQueryExample {
      @State color: string = '#DB7093';
      @State text: string = 'Portrait';
      @State dir: FlexDirection = FlexDirection.Column
      @State textHeight: string = "30%"
      @State textWidth: string = "100%"
      // 当设备横屏时条件成立
      listener: mediaquery.MediaQueryListener =
        this.getUIContext().getMediaQuery().matchMediaSync('(orientation: landscape)');
    
      // 当满足媒体查询条件时,触发回调
      onPortrait(mediaQueryResult: mediaquery.MediaQueryResult) {
        if (mediaQueryResult.matches as boolean) { // 若设备为横屏状态,更改相应的文本内容与字体颜色
          this.color = '#FFD700';
          this.text = 'Landscape';
          this.dir = FlexDirection.Row;
          this.textHeight = "100%"
          this.textWidth = "33%"
        } else {
          this.color = '#DB7093';
          this.text = 'Portrait';
          this.dir = FlexDirection.Column;
          this.textHeight = "33%"
          this.textWidth = "100%"
        }
      }
    
      aboutToAppear() {
        // 绑定当前应用实例
        // 绑定回调函数
        this.listener.on('change', (mediaQueryResult: mediaquery.MediaQueryResult) => {
          this.onPortrait(mediaQueryResult)
        });
      }
    
      aboutToDisappear() {
        // 解绑listener中注册的回调函数
        this.listener.off('change');
      }
    
      // 改变设备横竖屏状态函数
      private changeOrientation(isLandscape: boolean) {
        // 获取UIAbility实例的上下文信息
        let context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext;
        // 调用该接口手动改变设备横竖屏状态
        window.getLastWindow(context).then((lastWindow) => {
          lastWindow.setPreferredOrientation(isLandscape ? window.Orientation.LANDSCAPE : window.Orientation.PORTRAIT)
        });
      }
    
      build() {
        Column({ space: 30 }) {
          Text(this.text).fontSize(24).fontColor(this.color)
          Text('Landscape').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
            .onClick(() => {
              this.changeOrientation(true);
            })
          Text('Portrait').fontSize(50).fontColor(this.color).backgroundColor(Color.Orange)
            .onClick(() => {
              this.changeOrientation(false);
            })
          Flex({ direction: this.dir, space: { main: LengthMetrics.vp(10), cross: LengthMetrics.vp(5) } }) {
            Text('1')
              .height(this.textHeight)
              .width(this.textWidth)
              .textAlign(TextAlign.Center)
              .backgroundColor('rgb(0, 74, 175)')
            Text('2')
              .height(this.textHeight)
              .width(this.textWidth)
              .textAlign(TextAlign.Center)
              .backgroundColor('rgb(39, 135, 217)')
            Text('3')
              .height(this.textHeight)
              .width(this.textWidth)
              .textAlign(TextAlign.Center)
              .backgroundColor('rgb(240, 250, 255)')
          }.layoutWeight(1)
          .width("100%")
        }
        .width('100%').height('100%')
      }
    }

    图3 竖屏

    图4 横屏

    除此之外,kit前缀的接口还完美适配Stage模型,和API20的其他新特性无缝衔接,比如我们可以用@kit.ArkUI的状态管理能力,实现视频播放状态和界面布局的实时同步,在多端切换时,视频播放进度不会丢失,界面布局也能瞬间适配,多端体验的“一致性”直接拉满,这在大赛中,绝对是加分项。

    三、实战迁移:视频应用从ohos到kit,RK3568实测全流程(附避坑指南)

    光说不练假把式,结合本次大赛的要求,我们以这款视频应用为例,结合AtomGit老师和小助手的指导,详细说说从ohos到kit的迁移步骤,全程基于润和RK3568开发板实测,所有避坑点都是我们踩过的血泪教训,各位参赛战友可直接抄作业!

    核心迁移原则:接口替换、逻辑优化、多端适配统一,重点迁移媒体查询、组件渲染、视频播放三大模块,同时保留“一次开发,多端部署”的核心能力,确保迁移后,在手机、折叠屏、RK3568大屏端都能正常运行、流畅显示。

    第一步:环境升级,给项目“换个新引擎”

    迁移前,先完成环境和项目的基础配置升级,这一步是前提,千万不能漏,否则kit接口根本调不通——这也是我们一开始踩的第一个坑,没升级SDK就直接替换接口,结果报错一大堆,还好小助手及时提醒我们。

    这里欢迎大家看我们之前的分享

    【开源鸿蒙开发板应用升级适配大赛】第一篇:入门篇——润和Dayu200(RK3568)搭载OpenHarmony 6.0从API9到API20升级基础https://blog.csdn.net/weixin_31236533/article/details/157184823

    第二步:核心接口迁移,从ohos到kit的“一键替换+细节优化”

    这是本次迁移的核心步骤,重点是将原来的ohos前缀接口,替换成对应的kit前缀接口,同时优化逻辑,减少冗余代码,这里我们以最关键的3个模块为例,结合代码片段,说说迁移要点(附AtomGit老师指导的优化技巧)。

    媒体查询(mediaquery)迁移,从@ohos.mediaquery到@kit.ArkUI

    这是多端适配的核心,也是迁移过程中最容易出错的地方,原来的ohos.mediaquery接口,和API20的kit.ArkUI接口,不仅路径不同,部分方法的参数也有优化,直接复制粘贴会报错。

    原来API9(ohos)的代码(痛点满满):

    // API9 旧代码(ohos前缀)
    import mediaQuery from '@ohos.mediaquery';
    import { CommonConstants as Const } from '../constants/CommonConstants';
    
    export class BreakpointSystem {
      private currentBreakpoint: string = Const.MD;
      // 旧接口:ohos.mediaquery.matchMediaSync
      private smListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(Const.BREAKPOINTS_SCOPE_1);
      private mdListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(Const.BREAKPOINTS_SCOPE_2);
      private lgListener: mediaQuery.MediaQueryListener = mediaQuery.matchMediaSync(Const.BREAKPOINTS_SCOPE_3);
    
      // 旧回调参数:mediaQuery.MediaQueryResult
      private isBreakpointSM = (mediaQueryResult: mediaQuery.MediaQueryResult) => {
        if (mediaQueryResult.matches) {
          this.updateCurrentBreakpoint(Const.SM);
        }
      }
      // 其余方法省略...
    }

    迁移后API20(kit)的代码(优化后):

    // API20 新代码(kit前缀,结合老师指导优化)
    import { mediaquery } from '@kit.ArkUI'; // 接口统一归入@kit.ArkUI
    import { CommonConstants as Const } from '../constants/CommonConstants';
    
    export class BreakpointSystem {
      private currentBreakpoint: string = Const.MD;
      // 新接口:kit.ArkUI.mediaquery.matchMediaSync
      private smListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(Const.BREAKPOINTS_SCOPE_1);
      private mdListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(Const.BREAKPOINTS_SCOPE_2);
      private lgListener: mediaquery.MediaQueryListener = mediaquery.matchMediaSync(Const.BREAKPOINTS_SCOPE_3);
    
      // 新回调参数:mediaquery.MediaQueryResult(统一命名规范)
      private isBreakpointSM = (mediaqueryResult: mediaquery.MediaQueryResult) => {
        if (mediaqueryResult.matches) {
          this.updateCurrentBreakpoint(Const.SM);
        }
      }
      // 其余方法省略...
    }

    避坑要点(AtomGit老师重点强调):① 接口路径一定要改对,从@ohos.mediaquery改为@kit.ArkUI.mediaquery,并且要注意命名规范,mediaquery首字母小写;② 回调参数的命名要和接口保持一致,避免出现“参数类型不匹配”的报错;③ 迁移后,一定要在RK3568上测试断点响应速度,确保多端切换时,布局能瞬间刷新。

    第三步:多端适配优化,RK3568实测调试(避坑关键)

    接口迁移完成后,重点是多端适配的优化和调试,这一步是大赛评分的关键,也是我们最感谢AtomGit老师和小助手的地方——他们帮我们排查了很多RK3568上的适配bug,给出了很多实用的优化建议。

    1. 断点阈值优化:针对RK3568大屏设备,自定义断点阈值,设置lg(大屏)的宽度阈值为840vp,确保RK3568上的布局合理,轮播图显示3个、视频列表显示2列,充分利用屏幕空间;手机端sm阈值320vp-520vp,显示1个轮播图、1列视频列表,折叠屏md阈值520vp-840vp,显示2个轮播图、1列视频列表,一套代码适配三大设备形态。

    2. RK3568专属优化:① 解决组件卡顿问题,关闭不必要的动画效果,优化组件渲染顺序;② 修复视频播放和布局脱节的问题,用@kit.ArkUI的状态管理,实现播放状态和界面布局的实时同步;③ 适配RK3568的遥控器操作,新增遥控器按键监听,支持遥控器控制视频播放、暂停、切换,提升大屏体验。

    四、迁移总结:从ohos到kit,多端适配终于“告别凑活”

    折腾完这款视频应用的迁移,我们最大的感受就是:API20的kit前缀接口,不是对ohos接口的简单替换,而是对多端开发能力的“降维升级”。从原来的“接口散乱、多端凑活”,到现在的“接口统一、丝滑适配”,不仅减少了冗余代码,提升了开发效率,还让这款视频应用在RK3568上,真正发挥出了开发板的算力优势,多端体验也实现了质的飞跃。

    更重要的是,在这次迁移过程中,AtomGit老师和小助手的指导,帮我们少走了很多弯路,从代码规范到避坑技巧,从多端适配思路到RK3568的实测优化,每一个建议都切中要害。对于参加本次开源鸿蒙开发板应用升级适配大赛的战友们来说,视频应用的多端部署迁移,核心就是抓住“接口统一、逻辑优化、多端适配”三个关键点,充分利用API20的kit接口优势,结合开发板的硬件特性,就能做出兼顾功能、流畅度和美观度的参赛作品。

    目前,我们的视频应用已经完成全部迁移,在RK3568上跑起来,界面流畅、多端适配完美,视频播放清晰无卡顿,总算能安心冲刺大赛奖项了。下一篇,我们将唠唠迁移过程中的常见bug汇总,帮各位战友避坑避雷,关注我,大赛适配不踩坑,一起在开源鸿蒙的赛道上,拿奖到手软~

    最后,再次感谢AtomGit的老师和小助手,没有你们的专业指导,我们的迁移之路,恐怕还要多走很多弯路!

    欢迎大家访问我们团队的开源项目

    https://gitcode.com/JaneConan/Multi_device_V2

    并为我们投出宝贵的一票吧~~~

    https://competition.gitcode.com/competition/1995675881959841794/voting

    谢谢~ 

    Logo

    作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

    更多推荐