少湖说 | 科技自媒体

互联网,科技,数码,鸿蒙

创建应用

新建项目

1.在 AppGalleryConnect,我的项目,添加项目。

alt text

新建APP ID

1.在 AppGalleryConnect,证书、APP ID 和 Profile 中。

alt text

选择所属项目

alt text

新建应用/元服务

在 APP ID 右侧,点击发布, 会弹出发布 HarmonyOS应用/元服务

alt text

按提示填写应用信息,上传应用图标,选择应用分类、标签,点击下一步

上传打包 App(见下方)。

填写应用信息,公司信息等。需要注意的是,App 需要提供软著,元服务则不需要。

打包签名

自动签名

如果是开发和测试,可以使用自动签名。(File -> Project Structure -> Signing Configs 勾选 Automatically generate signature)

如果是打包上架,则需要手动签名

打开Deveco,依次执行以下操作

  1. 在主菜单栏单击 Build > Generate Key and CSR

alt text

2.如果没有创建 Key Store,则点击 New 创建一个

alt text

选择需要存储的目录(文件扩展名使用.p12),输入密码8位以上的复杂密码,点击 OK。

3.设置 Key 的别名

alt text

点击 Next 进入下一步

alt text

设置 CSR 存储位置,注意文件名需要用 .csr 做为扩展名。

点击 Finished, 会创建 .p12密钥库文件以及 证书请求.csr 等文件。

alt text

登录 AppGallery Connect, 执行以下操作

1.点击 证书、App ID 和 Profile,在证书一栏,点击“新增证书”,上传上面创建的 CSR 文件,将生成 cer 文件,

点击下载按钮,将该证书文件保存,待 APP 打包使用。

alt text

2.证书、App ID 和 Profile 中的 Profile一栏,点击添加,创建 Profile 文件,选择刚才创建的证书

alt text

打包 App

在 Deveco中,打开 File -> Project Structure -> Signing Configs, 默认会有一个创建过的 default 签名配置,点击 + 添加按钮,

alt text

以此选择上面创建的 .p12文件、.p7b文件,以及下载的 .cer 文件,输入密码, 点击 Apply 生成配置。

alt text

点击红框处的 Product 🔘 按钮,Build model 选择 release, 同时 build-profile.json5 文件中配置使用哪个签名

1
2
3
4
5
6
"products": [
{
"name": "default",
"signingConfig": "release",
}
]

alt text

点击 Build-> Build Hap(s)/APP(s), 选择 Build APP(s), 开始 APP 打包。

打包成功后,会在项目根目录的 build/outputs/default 生成 .app文件,即为我们要在应用市场上传的文件包

常见问题

软件包解析失败,详细信息点击软件包管理列表中的错误码查看,请按照指导重新打包上传。
993,Profile文件非法

检查签名配置文件,是否使用是 release 配置

build-profile.json5

如下面的配置,这里添加了两个签名配置,一个用于开发调度,一个用于打包上架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
"signingConfigs": [
{
"name": "default",
"type": "HarmonyOS",
"material": {
"certpath": "xx",
"storePassword": "xxx.cert",
"keyAlias": "debugKey",
"keyPassword": "xxx",
"profile": "xxx.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "xxx.p12"
}
},
{
"name": "release",
"type": "HarmonyOS",
"material": {
"certpath": "xx",
"storePassword": "xxx.cert",
"keyAlias": "release",
"keyPassword": "xxx",
"profile": "xxx.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "xxx.p12"
}
}
],
"products": [
{
"name": "default",
"signingConfig": "release",
"compatibleSdkVersion": "5.0.0(12)",
"runtimeOS": "HarmonyOS",
"buildOption": {
"strictMode": {
"caseSensitiveCheck": true,
"useNormalizedOHMUrl": true
}
}
}
],

在打包上架时,需要确保上面的 signingConfig 使用的是 name 为 release的签名配置

参考资料

-应用/服务签名

引言

在之前的文章鸿蒙Flutter实战:09-现有Flutter项目支持鸿蒙中,介绍了如何改造项目,适配鸿蒙平台。

文中讲述了整体的理念和思路,本文更进一步,结合可实操的项目代码,详细说明如何实施。

通过模块化、鸿蒙壳工程,结合 FVM 管理多版本 Flutter SDK,最终,保持原 Flutter 代码纯净,最小化修改,完成了鸿蒙化的适配示例。

本项目代码地址: https://gitee.com/zacks/flutter-ohos-demo

准备工作

1.安装 FVM 和 melos

安装 FVM,更多安装方式参考 fvm 官方文档

1
curl -fsSL https://fvm.app/install.sh | bash

安装melos

1
dart pub global activate melos

2.使用 FVM 安装 Flutter SDK

分别安装官方的3.22版本,以及鸿蒙社区的 3.22.0 版本

3.搭建 Flutter鸿蒙开发环境

参考文章《鸿蒙Flutter实战:01-搭建开发环境》

搭建项目架构

创建目录

1
2
# 创建项目目录
mkdir flutter-ohos-demo

设置使用的 Flutter SDK 版本
推荐在 VsCode 的命令行中执行以下命令,这将创建 .fvm 目录, .vscode/setting.json 文件, 和.fvmrc 文件

1
fvm use 3.22.0

初始化工作区间

创建目录,项目结构如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.
├── packages
│ ├── apps #该目录用于存放各端应用壳工程
│ ├── common #该目录用于存放公共库,均为纯 dart 代码,不依赖于 ios/android 等原生实现
│ │ ├── domains #领域对象,存放各类实体文件,如枚举/模型/vo/事件等
│ │ ├── extensions #存放扩展类文件,对于类的扩展方法/属性
│ │ ├── services #服务类:如请求服务/授权服务/缓存服务/平台调用服务/路由服务/工具类等
│ │ └── widgets #通用小型 widgets, 纯dart编写的 Flutter UI 组件
│ ├── components #封装组件库,可以依赖于第三方库/第三方插件,或依赖于 plugins中的插件
│ │ ├── image_uploader
│ │ └── player
│ ├── modules
│ │ ├── address
│ │ ├── home
│ │ ├── me
│ │ ├── message
│ │ ├── order
│ │ ├── shop
│ │ └── support
│ └── plugins #插件库,自行封装的插件库,依赖于原生平台(ios/android)的代码
│ └── printer
├── README.md
├── melos.yaml
└── pubspec.yaml

运行 melos bootstrap

1
melos bootstrap

开始编写代码

在各个 package 初始化代码,如在 packages/common/domains 目录运行

1
fvm flutter create --template package .

创建壳工程

新建两个壳工程,一个为 app,另外一个为 ohos_app

App 壳工程

进入 package/apps/app 目录, 创建 app 项目,该项目为一个 App 项目,用于各平台(ios/android/mac 等, 不包含鸿蒙)打包

1
fvm flutter create --template app --org com.shaohushuo.flutter app

增加依赖项

修改 pubspec.yaml,添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
services:
path: '../../common/services'
domains:
path: '../../common/domains'
widgets:
path: '../../common/widgets'

home:
path: '../../modules/home'
me:
path: '../../modules/me'
support:
path: '../../modules/support'

安装依赖

运行以下命令,安装依赖

1
fvm flutter pub get

鸿蒙壳工程

切换鸿蒙 Flutter SDK

首先在 flutter-ohos-demo 项目根目录,将 Flutter 版本切换到鸿蒙化的版本

1
fvm use custom_3.22.0

SDK 变更以后,需要重启 IDE (或者 Dart:Restart Analysis Server),以便让 Flutter 插件重启

创建 ohos_app 项目

进入 packages/apps 目录,创建 ohos_app 项目

1
fvm flutter create --template app --platforms ohos --org com.shaohushuo.flutter ohos_app

增加依赖项

进入 packages/apps/ohos_app 目录中的 pubspec.yaml, 同样增加依赖项

1
2
3
4
5
6
7
8
9
10
11
12
13
services:
path: '../../common/services'
domains:
path: '../../common/domains'
widgets:
path: '../../common/widgets'

home:
path: '../../modules/home'
me:
path: '../../modules/me'
support:
path: '../../modules/support'

三方库鸿蒙化适配

  1. 如果使用了 FVM,则编辑 pubspec.yaml 文件,增加以下配置,通过 dependency_overrides 来替换鸿蒙化的三方库,注意鸿蒙化的库与原库,保持版本统一
1
2
3
4
5
6
# 鸿蒙适配
dependency_overrides:
flutter_inappwebview:
git:
url: https://gitcode.com/openharmony-sig/flutter_inappwebview.git
path: "flutter_inappwebview"

编辑完运行 melos bootstrap, 这些更新 pubspec_overrides.yaml, 在里面添加相同的 dependency_overrides 内容。

  1. 如果没有使用 FVM,则直接编辑 pubspec_overrides.yaml 文件,没有则手动创建,添加以下内容:
1
2
3
4
5
6
# 鸿蒙适配
dependency_overrides:
flutter_inappwebview:
git:
url: https://gitcode.com/openharmony-sig/flutter_inappwebview.git
path: "flutter_inappwebview"

编辑完运行 flutter pub get 安装依赖。

以上两种方式执行成功后,观察 pubspec.lock 文件,可以发现,增加了类似 xxx_ohos 的插件依赖,本文中的例子为 flutter_inappwebview_ios

  1. 联合插件方式

除了上面使用 dependency_overrides 来配置鸿蒙适配库的两种方式以外,如果三方插件本身使用了联合插件的形式,也可以通过下面这种方式来添加鸿蒙平台的实现:

1
2
3
4
5
6
7
dependencies:

image_picker: ^1.1.2
image_picker_ohos:
git:
url: "https://gitcode.com/openharmony-sig/flutter_packages.git"
path: "packages/image_picker/image_picker_ohos"

这种方式称作 “未整合的联合插件”, 在上面的配置中,
image_picker 是一个联合插件, 这里直接使用官方社区的最新版本,观察该插件的 pubspec.yaml 的文件,通过其结构可以发现联合插件的特点, 该插件的依赖项为:

1
2
3
4
5
dependencies:
image_picker_platform_interface: ^2.10.0
...
image_picker_android: ^0.8.7
image_picker_ios: ^0.8.8

image_picker_platform_interface 是一个抽象层,它定义了平台相关的接口,下面是各个平台的实现,通过拆分成包,以依赖的方式加载,那同样的原理,就可以再添加一条鸿蒙平台的实现包,就可以完成鸿蒙化适配, 也就是上面案例中的 image_picker_ohos

1
2
3
4
image_picker_ohos:
git:
url: "https://gitcode.com/openharmony-sig/flutter_packages.git"
path: "packages/image_picker/image_picker_ohos"

运行调试

用 Deveco 打开apps/ohos_app/ohos 目录。

在 Deveco 左上角 打开 File -> Project Structure…, 点击 Siging Configs, 勾选 Automatically generate signature, 对鸿蒙项目签名。

在 ohos_app 目录下,使用 fvm flutter run,或者点击运行按钮,运行flutter项目。

注意事项

  1. melos.yaml 文件中的 sdkPath: .fvm/flutter_sdk 配置了 melos 使用的 flutter SDK 版本,即由FVM 配置的当前项目版本

  2. 每次切换 Flutter SDK 时,都会修改文件 .fvm/, vscode/settings.json 文件

  3. ohos_app/pubsec.yaml 中的 dependency_overrides, 仅添加需要鸿蒙化的三方库

  4. ohos-3.22 在 build 时,有的 har 包可能确实,建议保持 ohos-Flutter 版本最新,如果还是不行,可以考虑手动复制 har 包(使用 3.7 构建出来)

如何判断三方库是否需要鸿蒙化,简而言之,如果三方库由纯 Dart 实现,则不需要单独适配,直接使用;如果三方库依赖系统底层实现,则需要鸿蒙化适配。

三方库的适配情况,可以查询 Gitee/Github,或者查阅表格 Flutter三方库适配计划

  1. 已知插件删除问题,如果删除插件,可能需要在ohos里面手动修改代码,移除相关依赖

ohos/oh-package.json5

应用截图

首页 我的 帮助 播放器

插件使用示例

插件名 截图 使用示例
flutter_inappwebview 查看代码
video_player 查看代码
cached_network_image 查看代码
image_picker 查看代码

总结

  1. 通过 FVM 管理多个 Flutter SDK 版本,仅在鸿蒙调测打包时,切换到 ohos-flutter SDK
  2. 通过 apps 壳工程,将鸿蒙化适配的代码,尽量在 ohos_app 项目中完成。通过 pub 包管理的 dependency_overrides 配置,逐个替换鸿蒙化的三方库
  3. 通过 melos 管理多包项目,Flutter 项目进行模块化、组件化、插件化拆分,职责分离,平台抽象,不同平台组合打包,有效解决平台不一致问题

公众号

少湖说:少胡说,多观察。

关注公众号,加入交流群。

参考资料

Flutter 技术原理

Flutter 是一个主流的跨平台应用开发框架,基于 Dart 语言开发 UI 界面,它将描述界面的 Dart 代码直接编译成机器码,并使用渲染引擎调用 GPU/CPU 渲染。

渲染引擎的优势

使用自己的渲染引擎,这也是 Flutter 与其他跨平台框架最大的区别。

与 React Native 等高度依赖原生组件的框架不同,Flutter 摆脱了原生组件依赖,界面布局更加灵活,多端展示效果高度一致。由于渲染引擎自建,性能优化空间更大,这也是为什么Flutter 以流畅著称。

Impeller 渲染引擎由来

Flutter 的渲染引擎经历了多次迭代,早期全端使用 Skia, 后来为了解决 iOS 上着色器编译卡顿问题,Flutter 团队开发了新一代渲染引擎 Impeller。由于表现优异,Impeller 已经成为 Flutter 未来的发展方向。

渲染引擎将着色器编译成GPU指令,也就是二进制代码。

着色器是一种图形绘程序,它定义了如何绘制一个图形,比如颜色、形状、变换等。

Flutter在解决卡顿问题上,基于Skia做了多次努力和尝试,始终不尽人意,最终才有了Impeller。

Impeller 的设计目标包括: 消除首次卡顿、降低帧渲染驱动开销、利用现代 GPU 并行渲染能力

Impeller 与 Vulkan

Vulkan 是 OpenGL 推出的下一代图形 API,在安卓上,Flutter Impeller 调用 Vulkan 实现界面渲染。

  1. Impeller 实现了常用的着色器,支持参数化,通过预编译色器来避免编译卡顿问题
  2. 分层架构简化了渲染过程,每一层基于下层能力执行特定功能,高效且易用于维护更新
  3. 绘制命令容易聚合,易于拆分和并行
  4. 渲染设计与图形API解耦

Impeller 鸿蒙化

Impeller 的鸿蒙化基于 Vulkan, 通过 ArkUI 提供的 XComponent 组件承载 Flutter 视图。

与其他平台一样,通过 Method Channel , Dart 调用 ArkTS 来实现原生能力的调用。

XComponent 如何承载 Flutter 视图

通过 XComponet 获取到系统底层的 OHNativeWindow 实例, 这个就是鸿蒙的原生窗口,通过鸿蒙提供的扩展 VK_OHOS_surface, 将这个窗口转成一个 Vulkan 中的 VKSurface, 进而通过 VKSwapchain 实现了窗口绘制。

外接纹理:系统产生的图形图像如何嵌入 Flutter 显示

借助 XComponent,Flutter 绘制的界面可以显示在鸿蒙中。那么如果是非 Flutter 绘制的界面,如系统相机、视频等,如何嵌入到 Flutter 界面中呢?这就涉及到外接纹理了。

以相机为例,如果在 Flutter 中使用相机,当然最基础的可以通过 Method Channel 通信,将数据传递过来,但这个过程显然非常耗时,性能堪忧。另外一种方案是通过挖孔,叠加系统页面和 Flutter 画面,但这可能带来两套界面的操作、动画不一致问题。鸿蒙化采用了难度更高的数据导入方案,即将外部数据导入到 Flutter,将这些数据以纹理组件形式绘制。

鸿蒙化外接纹理涉及编码数据传输,如何解决性能问题

NativeBuffer 由鸿蒙提供, 通过它可以实现内存共享。那么外部导入的数据,通过 NativeBuffer,可以直接让 GPU 使用,避免了因数据拷贝造成的性能损失。

无论是 VKImage, 还是 GLTexture,都可以使用 NativeBuffer。

如何解决花屏问题

与安卓不同,Impeller 鸿蒙化方案采用了 GPU 硬件级的同步机制,保护数据读取,防止数据竞争,避免图像的花屏,通过 CPU 解耦,减少了空转时间,提高了性能。

Flutter 引擎侧不需要等待 Buffer 读写完成再调用绘制能力,只要保证 GPU 在绘制队列时,涉及 Buffer 数据的信号量状态为已读取即可,减少了 CPU 空等时间造成的性能损失。

渲染管线预加载

Impeller 通过预编译着色器避免了 Skia 中的运行时编译,但启动时需要加载这些着色器(也就是加载渲染管线),这就会出现明显的白屏时间。要解决这一问题,那就需要预加载渲染管线。

鸿蒙 Flutter 在运行时,首先创建 Dart 虚拟机解析 UI,在这个过程中同步进行渲染管线加载,从而实现首帧快速上屏,时延降低 50ms。

这部分不需要开发者操作,SDK 会自动完成。

混合开发加载优化:页面预加载

从原生 ArkUI 页面跳转到 Fluter 页面,需要先初始化 Flutter 引擎(这个过程耗时较长),再渲染首帧页面,这就会出现明显卡顿。

开发者可以提前手动调用 Flutter 引擎初始化 API,来解决这一卡顿问题。例如用户在触摸发生时,同步初始化 Flutter 引擎,等到用户抬手以后,就可以立即跳转到 Flutter 页面,整个过程会更加流程。

性能测试

通过预加载,可以节省一半的跳转时间。与 Skia 方案相比,Impeller 的转场流畅度提升显著。

与 Skia 方案相比,Impeller 方案在性能上表现更好。

鸿蒙 Flutter 的未来

鸿蒙 Flutter 适配团华为主导,计划每年推出 1-2 个比较大的版本,这两个大版本通过 fork 官方当时的主要版本来实现。至于能否将鸿蒙适配的部分合并到 Flutter 官方,还要看 Google 的态度。

结合前段时间 Flutter 社区版本 Flock 的推出,笔者持乐观态度。虽然 Google 在 Flutter 更新这方面进度缓慢,但社区版本带来不少想象力。多个 SDK 版本并存并不意味着分裂,竞争带来更多的活力。这点可以参考 Java,除了甲骨文主导的 OracleJDK, 还有 OpenJDK, AdoptOpenJDK 等十几种社区版本。

除了已有的插件开发方式,鸿蒙 Flutter 计划推出一种成本更低的方案,即通过一种统一接口描述,自动生成各端调用代码,省去开发者的编码工作。

总结

Flutter 作为一种流行的跨平台框架,支持鸿蒙是大势所趋。华为躬身入局,为 Flutter 社区带来了强劲动力。

从目前各方反馈来看,Flutter 应该是仅次于 ArkTs 的最佳适配方案。
不论是官方、还是开源社区、亦或是广大的开发者,都对 Flutter 有着强烈的诉求。
HarmonyOS 希望更多应用尽快适配鸿蒙,开发者或者是厂商,也希望以更低的成本最快上架;ArkTS 仍然有不少需要改进之处,就拿热重载一项与之对比,Flutter 不论从易用性、稳定性和成熟度已经遥遥领先。

对于平台独有的特性,如鸿蒙中的各种 Picker 组件、免权限按钮等,这与在其他平台上一样,是属于原生语言独有的优势所在。

虽然 Flutter 鸿蒙化已经初见成效,不少三方 Flutter 应用也快速适配上架,但生态建设仍需诸多时间,三方库还需要共建共享。

鸿蒙未来可期,Flutter 的鸿蒙化,为这个沉寂许久的框架,带来了新鲜血液。

参考资料

本文讲述如何通过 Flutter 开发鸿蒙原生应用。整个过程结合往期文章、实战经验、流程优化,体验丝滑、无痛。

无痛搭建开发环境

为了减少疼痛,这里使用全局唯一的 Flutter 版本开发。高阶用法可以参看往期同系列文章。

硬件准备

一台 Mac,一部 Mate60 Pro,两台显示器。

鸿蒙 Flutter SDK 开发人员爱用 Mac,使用相同设备(优先选择 Arm 版 Mac,X86 也可以,系统不要太老)无忧。

审核人员爱用 Mate60 系列,选择同款无忧。

显示器两台,左侧放别人的代码,右侧是自己的,方便参考(复制粘贴)。

环境准备

下载 DeEco

进入华为开发者联盟网站,https://developer.huawei.com/consumer/cn/download/,根据电脑机型点击下载。

下载 Flutter SDK

1
2
3
4
5
# 创建目录 ~/.fvm/versions
mkdir -p ~/.fvm/versions

# 克隆 Flutter SDK
git clone https://gitee.com/harmonycommando_flutter/flutter.git custom_3.22.0

配置环境变量

打开 ~/.bash_profile,增加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
# Flutter Mirror
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

# HarmonyOS SDK
export TOOL_HOME=/Applications/DevEco-Studio.app/Contents/
export DEVECO_SDK_HOME=$TOOL_HOME/sdk # command-line-tools/sdk
export PATH=$TOOL_HOME/tools/ohpm/bin:$PATH # command-line-tools/ohpm/bin
export PATH=$TOOL_HOME/tools/hvigor/bin:$PATH # command-line-tools/hvigor/bin
export PATH=$TOOL_HOME/tools/node/bin:$PATH # command-line-tools/tool/node/bin

export PATH="~/.fvm/versions/custom_3.22.0/bin:$PATH"

配置完成以后,关闭命令行工具,并重新打开,使之生效。使用 flutter doctor 命令查看是否有 3.22.0-ohos 字样。

创建项目

使用以下命令创建项目

1
flutter create --platforms ohos .

配置自动签名

使用 Deveco 打开项目,Mate60 Pro 手机连上电脑(必须)。

打开 File -> Project Structure -> Signing Configs 勾选 Automatically generate signature

点击 Apply 应用,OK 完成签名。此时可关闭 DevEco。

alt text

运行项目

  1. 使用 Flutter run 命令,或者在 IDE 中,点击运行按钮
1
2
# 查看设备编号
futter devices

出现以下类似输出

1
2
3
4
Found 3 connected devices:
FMR0224904009635 (mobile) • FMR0224904009635 • ohos-arm64 • Ohos OpenHarmony-5.0.1.115 (API 13)
macOS (desktop) • macos • darwin-x64 • macOS 14.6.1 23G93 darwin-x64
Chrome (web) • chrome • web-javascript • Google Chrome 131.0.6778.108

找到鸿蒙设备的 ID,如上所示为 FMR0224904009635。

1
2
# 运行
flutter run -d FMR0224904009635
  1. 在 IDE 中,点击运行按钮,以下是 VsCode 中的入口

确保安装了 Code Runner 插件

alt text

参考资料

在上期文章中,我们体验了无痛使用 Flutter 快速启动开发的过程,本期重点聚焦上架审核流程。

alt text

无痛打包

提前准备好生产证书。

具体步骤见“鸿蒙Flutter实战:13-鸿蒙应用打包上架流程”,在AGC平台证书栏,“新增证书”成功后,点击现在,得到一个xx.cer文件(数字证书文件);在Profile一栏,添加完之后,点击现在可以得到一个xx.p7b文件(Profile文件)。这两个文件将会在手动签名时使用。

使用最新的 IDE 自动上传功能。

DevEco Studio菜单栏,点击Build > Upload Product,登录开发者账号,进入上传界面。

alt text

选择 Generate app package and upload it to AppGallery Connect for test and publish。

无痛审核

审核实际上是一直非常重要的过程,不要以为开发完、打包上传就结束了,App Gallery 的审核十分严格,提前做好心理准备。

对于新应用,首次上架相对友好宽松,但也要明确没有明显问题,符合大致要求,不要存在侥幸心理。

名称

名字要确保唯一,提前在商城搜索,另外不要使用容易误解、太通用的的名称,如“文件管理”,“二维码生成”,建议增加品牌前缀,如有商标版权更加无痛。

图标

按规范设计,不要和系统图标相似,不要和其他应用雷同。

备案

提前对域名、App进行备案,应用名称、包名确保一一对应。

软著

使用电子软著,或者直接找中介全包,只需要提供名字等基础信息即可,全程无痛。

软著中的名称或简称要与App名称对应。

充分测试

确保充分测试,如果分发平台选择了平板,确保在平板上测试。

录制测试视频

审核人员测试很仔细,会帮你找bug,所以有测试视频也挺重要。
如果不具备平板测试条件,建议后期更新版本再勾选。上架只关注手机更无痛。

备注说明

如果应用有些特殊的设计理念,需要备注说明。不要指望审核人员能轻松理解你的意图。

测试账号

如果设计登录功能,最好提供一个可以正常使用的、甚至是有数据的测试账号,让审核人员也无痛。

提交时间

提前提交,避开周六。建议提交时间为周日-周四的每个晚上 。提交以后不要撤回,不然要重新排队。

参考资料

引言

在对插件鸿蒙化时,除了往期文章现有Flutter项目支持鸿蒙II中讲到的使用 dependency_overrides 来配置鸿蒙适配库的两种方式以外,如果三方插件本身使用了联合插件的形式,也可以通过下面这种方式来添加鸿蒙平台的实现:

1
2
3
4
5
6
7
dependencies:

image_picker: ^1.1.2
image_picker_ohos:
git:
url: "https://gitcode.com/openharmony-sig/flutter_packages.git"
path: "packages/image_picker/image_picker_ohos"

这也是一种非常优雅的方式,不用修改插件的代码,直接在项目配置中增加鸿蒙适配库即可。

什么是联合插件?

Federated plugins (联合插件) 是一种将对不同平台的支持分为单独的软件包。所以,联合插件能够使用针对 iOS、Android、Web 甚至是针对汽车 (例如在 IoT 设备上)分别使用对应的 package。除了这些好处之外,它还能够让领域专家在他们最了解的平台上扩展现有平台插件。

联合插件需要以下 package:

面向应用的 package

该 package 是用户使用插件的的直接依赖。它指定了 Flutter 应用使用的 API。

平台 package

一个或多个包含特定平台代码的 package。面向应用的 package 会调用这些平台 package—— 除非它们带有一些终端用户需要的特殊平台功能,否则它们不会包含在应用中。

平台接口 package

将面向应用的 package 与平台 package 进行整合的 package。该 package 会声明平台 package 需要实现的接口,供面向应用的 package 使用。使用单一的平台接口 package 可以确保所有平台 package 都按照各自的方法实现了统一要求的功能。

什么是未整合的联合插件?

相对的,整合的联合插件,也就是说插件在某个平台的实现,被整合进了主package,也就是”面向应用的 package”。如果插件已经整合了ohos实现,如 fluwx,则直接使用即可,无需再添加鸿蒙平台的实现。

如果插件没有整合ohos实现,如 image_picker, 则需要添加鸿蒙平台的实现。:

这种方式称作 “未整合的联合插件”, 在上面的配置中,
image_picker 是一个联合插件, 这里直接使用官方社区的最新版本,观察该插件的 pubspec.yaml 的文件,通过其结构可以发现联合插件的特点, 该插件的依赖项为:

1
2
3
4
5
dependencies:
image_picker_platform_interface: ^2.10.0
...
image_picker_android: ^0.8.7
image_picker_ios: ^0.8.8

image_picker_platform_interface 是一个抽象层,它定义了平台相关的接口,下面是各个平台的实现,通过拆分成包,以依赖的方式加载,那同样的原理,就可以再添加一条鸿蒙平台的实现包,就可以完成鸿蒙化适配, 也就是上面案例中的 image_picker_ohos

1
2
3
4
image_picker_ohos:
git:
url: "https://gitcode.com/openharmony-sig/flutter_packages.git"
path: "packages/image_picker/image_picker_ohos"

注意事项

需要注意,并非所有的插件都适合这种方式,有两种情况并不适合:

1.插件并非联合插件,也就是将所有平台的实现聚合在一个 package 中,这种建议使用 dependence_override 来覆写插件依赖

2.插件虽然是联合插件,但是鸿蒙化需要修改抽象层代码,典型的如用了 Platform.ohos 这种只有鸿蒙 Flutter SDK 才有的 API,这种也建议使用 dependence_override

前言

在之前的文章现有Flutter项目支持鸿蒙II中,介绍了如果使何第三方插件,同时给出了非常多的使用案例,如
flutter_inappwebview,video_player, image_picker 等,本文将开始介绍如何集成高德地图。

整体方案

通过 MethodChannel 进行消息通信,在 Dart 侧调用原生API,在 ArkTS 侧收到相关调用后,根据参数跳转到指定页面

Dart 侧

1
2
3
4
5
static Future<dynamic> redirectNative(String url) {
return _methodChannel.invokeMethod("redirectNative", {
"url": url,
});
}

ArkTS 侧

ohos/entry/src/main/ets/entryability 创建 OhosPlugin.ets 文件,这里收到到消息后,调用 router.pushUrl 方法跳转到指定页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default class OhosPlugin implements FlutterPlugin {
...
onAttachedToEngine(binding: FlutterPluginBinding): void {
this.channel.setMethodCallHandler({
onMethodCall : (call: MethodCall, result: MethodResult) => {
switch (call.method) {
case "redirectNative":
let url = String(call.argument("url"));
router.pushUrl({ url: url})
break;
default:
result.notImplemented();
break;
}
}
})
}
}

插件写好后,需要在 EntryAbility 中注册:

1
this.addPlugin(new OhosPlugin())

添加原生页面,回到 DevEco,在 pages 目录右键,创建一个空页面, 命名为 Amap

alt text

ohos/entry/oh-package.json 文件中引入高德地图SDK:

1
2
3
4
5
"dependencies": {
"@amap/amap_lbs_common": ">=1.1.0",
"@amap/amap_lbs_map3d": ">=2.1.1",
...
}

调用高德地图SDK,显示地图组件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { AMap, MapsInitializer, MapView, MapViewComponent, MapViewManager, } from '@amap/amap_lbs_map3d';
// 配置 API KEY
MapsInitializer.setApiKey("xxx");
MapViewManager.getInstance().registerMapViewCreatedCallback((mapview?: MapView, mapViewName?: string) => {
if (!mapview) {
return;
}
let mapView = mapview;
mapView.onCreate();
mapView.getMapAsync((map) => {
let aMap: AMap = map;
})
})

@Entry
@Component
struct Index {
build() {
Row() {
MapViewComponent()
.width('100%')
.height('100%')
}
}
}

调用

1
PlartformCall.redirectNative('pages/Amap');

注意事项

如果在运行时,遇到以下错误, 根据官方提醒, 需要配置 useNormalizedOHMUrl

1
ERROR: Bytecode HARs: [@amap/amap_lbs_map3d, @amap/amap_lbs_common] not supported when useNormalizedOHMUrl is not true.

打开文件 /ohos/build-profile.json5, 添加以下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"app": {
"products": [
{
"buildOption": {
"strictMode": {
"useNormalizedOHMUrl": true
}
}
}
]
}
}

截图

alt text

源码

https://gitee.com/zacks/flutter-ohos-demo

参考资料

本文以同层渲染为例,介绍如何集成高德地图

完整代码见 Flutter 鸿蒙版 Demo

概述

Dart 侧

核心代码如下,通过 OhosView 来承载原生视图

1
2
3
4
5
6
OhosView(
viewType: 'com.shaohushuo.app/customView',
onPlatformViewCreated: _onPlatformViewCreated,
creationParams: const <String, dynamic>{'initParams': 'hello world'},
creationParamsCodec: const StandardMessageCodec(),
)

其中 viewType 为自定义的 ohosView 的名称,onPlatformViewCreated 为创建完成回调,creationParams 为创建时传入的参数,creationParamsCodec 为参数编码格式。

ArkTS 侧

这里面我们按照《如何使用PlatformView》中的示例操作,首先需要创建一个显示高德地图的视图,其核心代码如下:

完整文件 AmapWidgetFactory.ets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

MapsInitializer.setApiKey("e4147e927a1f63a0acff45cecf9419b5");
MapViewManager.getInstance().registerMapViewCreatedCallback((mapview?: MapView, mapViewName?: string) => {
if (!mapview) {
return;
}
let mapView = mapview;
mapView.onCreate();
mapView.getMapAsync((map) => {
let aMap: AMap = map;
})
})

@Component
struct ButtonComponent {
@Prop params: Params
customView: AmapWidgetView = this.params.platformView as AmapWidgetView

build() {
Row() {
MapViewComponent().width('100%').height('100%')
}
}
}

接下来创建一个 AmapWidgetFactory.ets

1
2
3
4
5
6
7
8
9
10
11
12
export class AmapWidgetFactory extends PlatformViewFactory {
message: BinaryMessenger;

constructor(message: BinaryMessenger, createArgsCodes: MessageCodec<Object>) {
super(createArgsCodes);
this.message = message;
}

public create(context: common.Context, viewId: number, args: Object): PlatformView {
return new AmapWidgetView(context, viewId, args, this.message);
}
}

最终需要创建一个 AmapWidgetPlugin.ets

1
2
3
4
5
6
7
8
9
10
11
12
export class AmapWidgetPlugin implements FlutterPlugin {
getUniqueClassName(): string {
return 'AmapWidgetPlugin';
}

onAttachedToEngine(binding: FlutterPluginBinding): void {
binding.getPlatformViewRegistry()?.
registerViewFactory('com.shaohushuo.app/customView', new AmapWidgetFactory(binding.getBinaryMessenger(), StandardMessageCodec.INSTANCE));
}

onDetachedFromEngine(binding: FlutterPluginBinding): void {}
}

插件创建好之后,记得在 EntryAbility 中注册插件

1
this.addPlugin(new AmapWidgetPlugin())

需要注意的是,视图ID一定要两侧保持一致,如这里名为 ‘com.shaohushuo.app/customView’,否则无法正常显示

截图

alt text

参考资料

引言

在前面的系列文章中,我们从搭建开发环境开始,讲到如何使用、集成第三方插件,如何将现有项目进行鸿蒙化改造,以及上架审核等内容;还以高德地图的 HarmonyOS SDK 的使用为例,
讲解了如何将高德地图集成至项目当中。

混合开发

除了使用 Flutter 工程做为主项目开发以外,还有一种常见的开发方式,即混合开发,主项目工程为 HarmonyOS 工程, Flutter 工程以模块形式存在,,以依赖方式加载到主项目工程中,最终实现混合开发 。

想必混合开发也不会陌生,因为我们也简要介绍过,鸿蒙 Flutter 项目混合开发的两种形式。

从本章开始,我们将进一步深入,从原理出发、以工程实战为主线,探索这两种混合开发方式。

混合开发的两种方式

  1. 以 Har 包的方式加载到 HarmonyOS 工程

HAR(Harmony Archive)是静态共享包,可以包含代码、C++库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。

alt text

这种开发方式,就是将 Flutter 模块编译打包成 HAR 包,在原生鸿蒙项目中,以 Har 包的方式引入这个模块包,从而实现混合开发。

  1. 以源码的方式加载到 HarmonyOS 工程

从方式 1 的介绍中,可以发现,每次 Flutter 模块的更新都需要重新编译成 Har 包,并且需要重新打包到原生鸿蒙工程中,非常麻烦。于是就有了源码依赖的方式,也就是说,

让原生鸿蒙工程依赖 Flutter 模块的源码,这样 Flutter 代码变更时,不需要重新打包成 Har 包,而且可以在开发过程中,实现热更新,实时刷新界面。

开发流程

  1. 总目录

为了方便管理/演示,本示例的目录名为 ohos_flutter_module_demo, 我们将原生鸿蒙工程和 Flutter 模块都在这个目录下创建。

  1. 创建原生鸿蒙工程

这个也就是宿主工程,这里我们使用 DevEco Studio,在 ohos_flutter_module_demo 目录下面, 创建一个原生鸿蒙工程,本文中工程名命名为 ohos_app。

  1. 创建 Flutter 模块

这个流程都一样,我们可以使用命令以下命令创建一个 Flutter 模块:

1
flutter create --template=module my_flutter_module

最终项目目录结构如下:

1
2
3
ohos_flutter_module_demo
├── my_flutter_module
├── ohos_app

这样,为了方便维护,Flutter 模块在宿主项目外部创建,与宿主项目同级目录。本文中,上级目录为 ohos_flutter_module_demo, 其下有两个子目录,分别是 ohos_app (宿主项目),和 my_flutter_module (Flutter 模块)。

  1. 编译 Flutter 模块

如何使用 Har 包模式,需要先将 Flutter 模块编译成 Har 包;如何使用源码依赖的方式,则不需要这一步。

使用以下命令将 Flutter 模块编译成 Har 包:

1
flutter build har --debug
  1. 配置原生鸿蒙工程

如果是通过 Har 包模式,则可将 Har 包添加至依赖文件中:

首先先构建出的 Har 包复制到 ohos 鸿蒙工程中:

1
cp -r my_flutter_module/.ohos/har/* ohos_app/har/
1
2
3
4
5
6
7
"dependencies": {
"@ohos/flutter_module": "file:har/flutter_module.har",
"@ohos/flutter_ohos": "file:har/flutter.har"
},
"overrides" {
"@ohos/flutter_ohos": "file:har/flutter.har",
}

如果是通过源码模式,则需将 Flutter 模块的源码添加至依赖文件中:

1
2
3
"dependencies": {
"@ohos/flutter_module": "../flutter_module"
}
  1. 修改入口文件(可选)
    修改入口文件, 将 Flutter 模块生成的 .ohos目录中的 EntryAbility.ets 和 Index.ets 文件复制到宿主工程中进行替换
1
2
cp my_flutter_module/.ohos/entry/src/main/ets/entryability/EntryAbility.ets ohos_app/entry/src/main/ets/entryability/EntryAbility.ets
cp my_flutter_module/.ohos/entry/src/main/ets/pages/Index.ets ohos_app/entry/src/main/ets/pages/Index.ets

创建工作

创建一个根目录

1
mkdir ohos_flutter_module_demo

这个目录用于存放 flutter 项目和鸿蒙项目。

创建 Flutter 模块

首先创建一个 Flutter 模块,我们选择与 ohos_app 项目同级目录

1
flutter create --template=module my_flutter_module

如果使用了 fvm,首先确定当前目录使用的 flutter 版本为鸿蒙的 SDK 版本,如可以使用 fvm use custom_3.22.0设置,然后在 flutter 命令前加上 fvm,上面的命令也就变成了 fvm flutter create --template=module my_flutter_module

命令行出现以下输出:

1
2
3
4
5
6
7
8
Creating project my_flutter_module...
Resolving dependencies in `my_flutter_module`...
Downloading packages...
Got dependencies in `my_flutter_module`.
Wrote 12 files.

All done!
Your module code is in my_flutter_module/lib/main.dart.

创建 Flutter 模块成功之后,目录结构如下:

alt text

创建 DevEco 工程

使用 DevEco 在 ohos_flutter_module_demo 目录下,新建一个名为 ohos_app 的工程。

alt text

注意保存的目录为 xxxx/ohos_flutter_module_demo/ohos_app

创建成功后,整个目录结构如下:

alt text

可以看到,我们将 Flutter 模块放在了与 ohos_app 项目同级。my_flutter_module 中自动创建了 .ohos 目录, 这也是一个简单的鸿蒙项目,不过会包含一个名为 flutter_module 的模块。

将 Flutter 模块打包成 Har 包

接下来,我们使用 flutter build har 命令将 Flutter 模块打包成 Har 包。

打包前首先配置签名,用 DevEco 打开 .ohos 目录,然后对项目签名,操作如下:

1
DevEco Studio 打开 my_flutter_module/.ohos 工程后配置调试签名(File -> Project Structure -> Signing Configs 勾选 Automatically generate signature)
1
flutter build har --debug

命令行出现以下输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Running Hvigor task assembleHar...                                 47.5s

Consuming the Module
1. Open <host project>/oh-package.json5
2. Add flutter_module to the dependencies list:

"dependencies": {
"@ohos/flutter_module": "file:path/to/har/flutter_module.har"
}

3. Override flutter and plugins dependencies:

"overrides" {
"@ohos/flutter_ohos": "file:path/to/har/flutter.har",
}

观察目录 my_flutter_module/.ohos/har 目录,可以看到 Flutter 模块的 Har 包已经生成了, 里面生成了两个文件,分别是 flutter_module.har 和 flutter.har。

注意,生成的 flutter_module.har 是默认名称,与项目名无关。如何想要修改生成的名称,可在 my_flutter_module/.ohos/flutter_module/oh-package.json5 文件中修改包名。

alt text

引入 Har 包到 ohos 项目中

接下来,我们将生成的 har 包复制到宿主项目 ohos 中,然后回到 ohos 项目工程,将上面生成的 Har 包添依赖配置中。

  1. 复制 Har 包
1
cp -r my_flutter_module/.ohos/har/* ohos/har/
  1. 编辑 ohos_app/oh-package.json5 文件:
1
2
3
4
5
6
7
"dependencies": {
"@ohos/flutter_module": "file:har/my_flutter_module.har",
"@ohos/flutter_ohos": "file:har/my_flutter.har"
},
"overrides" {
"@ohos/flutter_ohos": "file:har/flutter.har",
}

注意:如何不想使用复制Har包的方式,也可以通过相对路径直接引入原Har的位置,可使用以下方式引入:

1
2
3
4
5
6
7
"dependencies": {
"@ohos/flutter_module": "file:../my_flutter_module/.ohos/har/flutter_module.har",
"@ohos/flutter_ohos": "file:../my_flutter_module/.ohos/har/flutter.har"
},
"overrides": {
"@ohos/flutter_ohos": "file:../my_flutter_module/.ohos/har/flutter.har"
},

这里需要配置 overrides ,为了解决依赖冲突问题,因为 @ohos/flutter_module依赖了 @ohos/flutter_ohos, 但因为使用的是相对目录,会导致加载失败,故这里通过 overrides 来重新指定 @ohos/flutter_ohos 的路径。

另外,与上文提示或者官方文档中不同的是,我们在 dependencies 也添加了 @ohos/flutter_ohos ,这是为了 IDE 提示的问题,不加的话会出现以下错误信息

1
Cannot find module '@ohos/flutter_ohos' or its corresponding type declarations. <ArkTSCheck>

最后, 再次对 ohos 项目签名,并运行 DevEco 项目。

接下来

现在我们只是将 Har 包引入到 ohos 项目中,在接下来的文章 跳转Flutter页面中,我们将介绍如何在 ohos 原生项目中,初始化 Flutter 引擎,并在合适的地方跳转打开 Flutter 页面。

总结

  1. 这种模式适合较大的项目团队,常见的场景是,负责 Flutter 开发的同事开发好指定的模块,以 Har 包的形式交付给鸿蒙原生的开发团队。

  2. 在这种模式下,鸿蒙原生的开发团队,不需要太多关注 Flutter 部分的内容,甚至不需要安装 Flutter 开发环境,可以更好的职责分离。

  3. 缺点,由于 Flutter 模块打包成了 Har 包,以 so 文件存在,故 Flutter 无法热重载。

参考资料

0%