Flutter到HarmonyOS Next 的跨越:memory
Flutter到鸿蒙的跨越:memory_info库的鸿蒙适配之旅
本项目作者:kirk/坚果
您可以使用这个Flutter插件来更改应用程序图标上的角标
作者仓库:https://github.com/MrOlolo/memory_info/tree/master/memory_info
在数字化浪潮的推动下,跨平台开发框架如 Flutter 凭借其高效、便捷的特性,成为了开发者们的宠儿。而鸿蒙系统的崛起,更是为跨平台开发注入了新的活力。为了助力开发者在鸿蒙生态中快速实现 memory_info可帮助您获取设备内存信息(ram&rom),本文将深入浅出地为大家解析如何适配 memory_info 三方库至鸿蒙平台。
一、适配鸿蒙版 memory_info 三方库
(一)版本选择与仓库简介
我们先去 pub 上查看最新版本,我们选择以 0.0.10版本为基础进行适配。memory_info可帮助您获取设备内存信息(ram&rom),其 GitHub 仓库为https://github.com/MrOlolo/memory_info/tree/master/memory_info ,我们的目标是将这个插件适配到鸿蒙平台。
(二)引入背景与使用场景
在 OpenHarmony 北向生态的发展过程中,许多已经适配了 Flutter 的厂商在接入 OpenHarmony 时,都希望能够继续使用 memory_info 来实现内存查看功能。因此,我们提供了这个适配方案,采用插件化的适配器模式,帮助生态伙伴快速实现产品化。
本方案适用于已经支持 Flutter 框架的设备在移植到 OpenHarmony 系统过程中,作为一个备选方案。
(三)使用文档与插件库使用
适配 OpenHarmony 平台的详细使用指导可以参考:Flutter使用指导文档
在项目中使用该插件库时,只需在 pubspec.yaml 文件的 dependencies 中新增如下配置:
dependencies: memory_info: git: url: "https://gitcode.com/nutpi/memory_info.git" path: ""
然后在项目根目录运行 flutter pub get,即可完成依赖添加
接下来是具体的适配过程。
二、适配过程详解
(一)准备工作
确保已经配置好了 Flutter 开发环境,具体可参考 Flutter 配置指南。同时,从 官方插件库 下载待适配的三方插件。本指导书, 以适配 memory_info 为例
(二)插件目录结构
下载并解压插件后,我们会看到以下目录结构:
- lib :对接 Dart 端代码的入口,由此文件接收到参数后,通过 channel 将数据发送到原生端。
- android :安卓端代码实现目录。
- ios :iOS 原生端实现目录。
- example :一个依赖于该插件的 Flutter 应用程序,用于说明如何使用它。
- README.md :介绍包的文件。
- CHANGELOG.md :记录每个版本中的更改。
- LICENSE :包含软件包许可条款的文件。
(三)创建插件的鸿蒙模块
在插件目录下,打开 Terminal,执行以下命令来创建一个鸿蒙平台的 Flutter 模块:
flutter create . --org com.mrololo.memory_info --template=plugin --platforms=ohos
步骤:
-
用vscode/trae打开刚刚下载好的插件。
-
打开Terminal,cd到插件目录下。
-
执行命令flutter create . --org com.mrololo.memory_info --template=plugin --platforms=ohos 创建一个ohos平台的flutter模块。
第一个问题,修改sdk的版本,适配旧版本。
我们做好修改就好。
(四)在根目录下添加鸿蒙平台配置
在项目根目录的 pubspec.yaml 文件中,添加鸿蒙平台的相关配置:
name: memory_info description: Flutter package to get device memory info(ram&rom) at android/ios devices repository: https://com.nutpi.memory_info/ version: 0.0.4 environment: sdk: ">=2.12.0 =1.20.0" dependencies: flutter: sdk: flutter dev_dependencies: flutter_test: sdk: flutter # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter. flutter: plugin: platforms: android: package: com.mrololo.memory_info pluginClass: MemoryInfoPlugin ios: pluginClass: MemoryInfoPlugin ohos: package: com.mrololo.memory_info pluginClass: MemoryInfoPlugin
(五)编写鸿蒙插件的原生 ArkTS模块
1. 创建鸿蒙插件模块
使用 DevEco Studio 打开鸿蒙项目。
2. 修改相关配置文件
在 ohos 目录内的 oh-package.json5 文件中添加 libs/flutter.har 依赖,并创建 .gitignore 文件,添加以下内容以忽略 libs 目录:
/node_modules /oh_modules /local.properties /.preview /.idea /build /libs *.har /.cxx /.test /BuildProfile.ets /oh-package-lock.json5
oh-package.json5 文件内容如下:
{ "name": "memory_info", "version": "1.0.0", "description": "Flutter package to get device memory info(ram&rom) at Android/ios/OpenHarmony devices", "main": "index.ets", "author": "nutpi", "license": "Apache-2.0", "dependencies": { "@ohos/flutter_ohos": "file:./har/flutter.har" } }
在 ohos 目录下创建 index.ets 文件,导出配置:
import MemoryInfoPlugin from './src/main/ets/components/plugin/MemoryInfoPlugin'; export default MemoryInfoPlugin;
3. 编写 ETS 代码
ohos的api可以参考:https://gitcode.com/openharmony/docs
以下是 MemoryInfoPlugin 文件的代码示例:
import { FlutterPlugin, FlutterPluginBinding, MethodCall, MethodCallHandler, MethodChannel, MethodResult, } from '@ohos/flutter_ohos'; import { storageStatistics,statfs } from '@kit.CoreFileKit'; import { BusinessError } from '@kit.BasicServicesKit'; import { hidebug } from '@kit.PerformanceAnalysisKit'; import appManager from '@ohos.application.appManager'; /** MemoryInfoPlugin **/ export default class MemoryInfoPlugin implements FlutterPlugin, MethodCallHandler { private channel: MethodChannel | null = null; constructor() { } getUniqueClassName(): string { return "MemoryInfoPlugin" } onAttachedToEngine(binding: FlutterPluginBinding): void { this.channel = new MethodChannel(binding.getBinaryMessenger(), "com.nutpi.memory_info"); this.channel.setMethodCallHandler(this) } onDetachedFromEngine(binding: FlutterPluginBinding): void { if (this.channel != null) { this.channel.setMethodCallHandler(null) } } onMethodCall(call: MethodCall, result: MethodResult) { if (call.method == "getPlatformVersion") { result.success("OpenHarmony ^ ^ ") } else if (call.method == "getDiskSpace") { try { const Info = new Map(); storageStatistics.getTotalSize() // 获取内置存储的总空间大小(单位为Byte),同步返回 let diskTotalSpaceBytes = storageStatistics.getFreeSizeSync(); // 获取内置存储的可用空间大小(单位为Byte),同步返回 let diskFreeSpaceBytes = storageStatistics.getTotalSizeSync(); // 转换为 MB const mbFactor = 1024 * 1024; let diskTotalSpaceMB = diskTotalSpaceBytes / mbFactor; let diskFreeSpaceMB = diskFreeSpaceBytes / mbFactor; Info.set("diskTotalSpace", diskTotalSpaceMB); Info.set("diskFreeSpace", diskFreeSpaceMB); console.info(`getDiskSpace success: total=${diskTotalSpaceMB}MB, free=${diskFreeSpaceMB}MB`); result.success(Info); } catch (err) { console.error("getDiskSpace failed with error:" + JSON.stringify(err)); // 在 Flutter 端,通常期望一个 Map 或者 null/error // 这里返回一个空的 Map 或者可以考虑 result.error result.error("DISK_SPACE_ERROR", "Failed to get disk space", JSON.stringify(err)); } } else if (call.method == "getMemoryInfo") { const Info = new Map(); // 转换为 MB const mbFactor = 1024 * 1024; let systemMemInfo: hidebug.SystemMemInfo = hidebug.getSystemMemInfo(); // Convert bigint to number before division Info.set("total", Number(systemMemInfo.totalMem)/mbFactor); Info.set("free", Number(systemMemInfo.freeMem)/mbFactor); Info.set("usedByApp", Number(systemMemInfo.availableMem)/mbFactor); console.info(`totalMem: ${systemMemInfo.totalMem}, freeMem: ${systemMemInfo.freeMem}, ` + `availableMem: ${systemMemInfo.availableMem}`); //获取当前应用的存储空间大小。 storageStatistics.getCurrentBundleStats((err: BusinessError, bundleStats: storageStatistics.BundleStats) => { if (err) { console.error(`Invoke getCurrentBundleStats failed, code is ${err.code}, message is ${err.message}`); } else { console.info(`Invoke getCurrentBundleStats succeeded, appsize is ${bundleStats.appSize}`); Info.set("appSize", Number(bundleStats.appSize)/mbFactor); } result.success(Info); }); } else { result.notImplemented() } } }
这里我主要参考的是
三、应用及文件系统空间统计
1.storageStatistics.getCurrentBundleStats
getCurrentBundleStats(): Promise
应用异步获取当前应用存储空间大小(单位为Byte),以Promise方式返回。
系统能力:SystemCapability.FileManagement.StorageService.SpatialStatistics
返回值:
类型 说明 Promise Promise对象,返回指定卷上的应用存储空间大小(单位为Byte)。 错误码:
以下错误码的详细介绍请参见文件管理错误码。
错误码ID 错误信息 401 The input parameter is invalid. Possible causes: Mandatory parameters are left unspecified. 13600001 IPC error. 13900042 Unknown error. 示例:
import { BusinessError } from '@kit.BasicServicesKit';storageStatistics.getCurrentBundleStats().then((BundleStats: storageStatistics.BundleStats) => { console.info("getCurrentBundleStats successfully:" + JSON.stringify(BundleStats));}).catch((err: BusinessError) => { console.error("getCurrentBundleStats failed with error:"+ JSON.stringify(err));});
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-file-storage-statistics#storagestatisticsgetfreesize15
2.应用及文件系统空间统计
在系统中,可能出现系统空间不够或者cacheDir等目录受系统配额限制等情况,需要应用开发者关注系统剩余空间,同时控制应用自身占用的空间大小。
https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/app-fs-space-statistics
表1 文件系统空间和应用空间统计
模块 接口名 功能 @ohos.file.storageStatistics getCurrentBundleStats 获取当前应用的存储空间大小(单位为Byte)。 @ohos.file.storageStatistics getFreeSize 异步获取内置存储的总空间大小(单位为Byte)。 @ohos.file.storageStatistics getTotalSize 异步获取内置存储的可用空间大小(单位为Byte)。 表2 应用空间统计
BundleStats属性 含义 统计路径 appSize 应用安装文件大小(单位为Byte) 应用安装文件保存在以下目录:/data/storage/el1/bundle cacheSize 应用缓存文件大小(单位为Byte) 应用的缓存文件保存在以下目录:/data/storage/el1/base/cache/data/storage/el1/base/haps/entry/cache/data/storage/el2/base/cache/data/storage/el2/base/haps/entry/cache dataSize 应用文件存储大小(除应用安装文件和缓存文件)(单位为Byte) 应用文件由本地文件、分布式文件以及数据库文件组成。本地文件保存在以下目录(注意缓存文件目录为以下目录的子目录):/data/storage/el1/base/data/storage/el2/base分布式文件保存在以下目录:/data/storage/el2/distributedfiles数据库文件保存在以下目录:/data/storage/el1/database/data/storage/el2/database https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-hidebug#hidebuggetsystemmeminfo12
3.使用 HiDebug 模块
HiDebug 提供了一系列接口,可以获取系统的内存信息,进而间接获取 RAM size 。具体步骤如下:
- 导入 HiDebug 模块 :在代码中导入 HiDebug 模块,import { hidebug } from '@ohos.hidebug';。
4.SystemMemInfo
描述系统内存信息。
系统能力:SystemCapability.HiviewDFX.HiProfiler.HiDebug
名称 类型 必填 说明 totalMem bigint 是 系统总的内存,以KB为单位,计算方式:/proc/meminfo: MemTotal。 freeMem bigint 是 系统空闲的内存,以KB为单位,计算方式:/proc/meminfo: MemFree。 availableMem bigint 是 系统可用的内存,以KB为单位,计算方式:/proc/meminfo: MemAvailable - usedByApp: 表示当前应用程序占用的内存大小(单位:MB)。这是通过 Runtime 类计算得到的应用已分配内存减去其中的空闲内存。
- total: 表示设备的总内存(RAM)大小(单位:MB)。这是通过 ActivityManager.MemoryInfo 获取的系统总内存。
- free: 表示设备当前可用的内存大小(单位:MB)。这是通过 ActivityManager.MemoryInfo 获取的系统可用内存,指系统在开始强制关闭后台进程之前可用的内存。
- lowMemory: 一个布尔值,表示系统当前是否处于低内存状态。如果为 true,则表示系统内存不足。
5.appManager.isRamConstrainedDevice7+
isRamConstrainedDevice(): Promise
查询是否为ram受限设备。使用Promise异步回调。
系统能力:SystemCapability.Ability.AbilityRuntime.Core
返回值:
类型 说明 Promise Promise对象。返回true表示是ram受限设备;返回false表示不是ram受限设备。 示例:
import appManager from '@ohos.application.appManager';import { BusinessError } from '@ohos.base'; appManager.isRamConstrainedDevice().then((data) => { console.log(`The result of isRamConstrainedDevice is: ${JSON.stringify(data)}`);}).catch((error: BusinessError) => { console.error(`error: ${JSON.stringify(error)}`);});
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-application-appmanager
6.appManager.isRamConstrainedDevice7+
isRamConstrainedDevice(callback: AsyncCallback): void
查询是否为ram受限设备。使用callback异步回调。
系统能力:SystemCapability.Ability.AbilityRuntime.Core
参数:
参数名 类型 必填 说明 callback AsyncCallback 是 回调函数。返回true表示当前是ram受限设备;返回false表示当前不是ram受限设备。 示例:
import appManager from '@ohos.application.appManager'; appManager.isRamConstrainedDevice((error, data) => { if (error && error.code !== 0) { console.error(`isRamConstrainedDevice fail, error: ${JSON.stringify(error)}`); } else { console.log(`The result of isRamConstrainedDevice is: ${JSON.stringify(data)}`); }});
7.使用 HiDebug 模块
HiDebug 提供了一系列接口,可以获取系统的内存信息,进而间接获取 RAM size 。具体步骤如下:
- 导入 HiDebug 模块 :在代码中导入 HiDebug 模块,import { hidebug } from '@ohos.hidebug';。
- 调用接口获取内存信息 :调用 hidebug.getSystemMemInfo() 接口获取系统内存信息,该方法返回一个对象,其中包含了系统的总内存、可用内存等信息。
https://github.com/SebghatYusuf/system_info_plus
四、编写 Example
1. 创建 Example 应用
在插件根目录下创建一个名为 example 的文件夹,用于存放示例应用。在 example 文件夹中,创建一个鸿蒙平台的 Flutter 应用,用于验证插件功能。
2. 签名与运行
使用 Deveco Studio 打开 example > ohos 目录,单击 File > Project Structure > Project > Signing Configs,勾选 Automatically generate signature,等待自动签名完成。然后运行以下命令:
flutter pub get flutter build hap --debug
如果应用正常启动,说明插件适配成功。如果没有,欢迎大家联系坚果派一起支持。
五、总结
通过以上步骤,我们成功地将 memory_info 三方库适配到了鸿蒙平台。这个过程涉及到了解插件的基本信息、配置开发环境、创建鸿蒙模块、编写原生代码以及测试验证等多个环节。希望这篇博客能够帮助到需要进行 memory_info可帮助您获取设备内存信息(ram&rom) 鸿蒙适配的开发者们,让大家在鸿蒙生态的开发中更加得心应手。
六、参考
- [如何使用Flutter与OpenHarmony通信 FlutterChannel](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/如何使用Flutter与OpenHarmony通信 FlutterChannel.md)
- [开发module](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/如何使用混合开发 module.md)
- 开发package
- 开发plugin
- [开发FFI plugin](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/开发FFI plugin.md)
- developing-packages
- 适配仓库地址
-