Flutter-OH插件fps鸿蒙适配实战:从零到一完整指南

欢迎大家加入开源鸿蒙跨平台社区

image-20260228104829279

前言

随着鸿蒙生态的快速发展,越来越多的Flutter开发者开始关注如何将现有的Flutter插件适配到鸿蒙平台。本文将以performance_fps插件为例,详细讲解从Flutter原生插件到鸿蒙平台的完整适配过程,包括技术选型、代码实现、构建配置等关键环节。

一、项目背景

performance_fps是一个用于实时计算应用FPS(每秒帧数)的Flutter插件,支持Android和iOS平台。它可以帮助开发者监控应用的性能表现,是性能优化的重要工具。我们的目标是将这个插件适配到鸿蒙平台,使其能够支持Flutter OpenHarmony(OH)应用。

原插件架构

原插件采用标准的Flutter插件架构:

performance_fps/
├── lib/                          # Dart层,提供统一的API接口
│   └── plugin/fps_plugin.dart    # MethodChannel通信层
├── android/                      # Android原生实现
│   └── src/main/java/com/lib/fps/fps/FpsPlugin.java
└── ios/                          # iOS原生实现
    └── Classes/FpsPlugin.m

核心功能

通过MethodChannel调用原生平台API获取屏幕刷新率:

// Dart层接口
static Future<double> get getRefreshRate async {
  final double fpsHz = await _channel.invokeMethod('getRefreshRate') ?? 60;
  return fpsHz;
}

二、鸿蒙平台适配技术选型

2.1 开发环境

在进行适配之前,需要准备以下开发环境:

  • Flutter SDK: 3.35.8-ohos-0.0.2 或更高版本
  • DevEco Studio: 6.1.0 或更高版本
  • HarmonyOS SDK: 6.0.1(21) 或更高版本

2.2 技术栈选择

鸿蒙平台适配主要涉及以下技术:

  1. 编程语言: ArkTS(TypeScript的鸿蒙方言)
  2. UI框架: ArkUI(鸿蒙声明式UI框架)
  3. 构建工具: Hvigor(鸿蒙构建系统)
  4. 插件框架: Flutter OHOS Plugin Framework
  5. 显示同步API: @kit.ArkGraphics2D 中的 displaySync

2.3 核心API对比

功能 Android iOS HarmonyOS
屏幕刷新率获取 WindowManager.getRefreshRate() CADisplayLink displaySync.DisplaySync
语言 Java Objective-C ArkTS
构建系统 Gradle CocoaPods Hvigor
插件框架 FlutterPlugin FlutterPlugin FlutterPlugin

三、项目结构调整

3.1 创建鸿蒙平台目录

在插件根目录下创建ohos目录,结构如下:

flutter_fps/
├── ohos/
│   ├── src/
│   │   └── main/
│   │       ├── ets/
│   │       │   └── components/
│   │       │       └── plugin/
│   │       │           └── FlutterFpsPlugin.ets
│   │       └── module.json5
│   ├── index.ets
│   ├── build-profile.json5
│   ├── oh-package.json5
│   └── hvigorfile.ts

3.2 配置pubspec.yaml

pubspec.yaml中添加鸿蒙平台配置:

flutter:
  plugin:
    platforms:
      android:
        package: com.lib.fps.fps
        pluginClass: FpsPlugin
      ios:
        pluginClass: FpsPlugin
      ohos:
        pluginClass: FpsPlugin

四、核心代码实现

4.1 插件入口文件

文件: ohos/index.ets

import FlutterFpsPlugin from './src/main/ets/components/plugin/FlutterFpsPlugin';
export default FlutterFpsPlugin;

这是插件的入口文件,Flutter构建系统会通过这个文件加载插件实现。

4.2 模块配置

文件: ohos/src/main/module.json5

{
  "module": {
    "name": "performance_fps",
    "type": "har",
    "deviceTypes": [
      "default",
      "tablet"
    ]
  }
}

关键配置说明:

  • name: 模块名称,需与插件名称保持一致
  • type: "har"表示这是一个HarmonyOS Archive(类似Android的AAR)
  • deviceTypes: 支持的设备类型

4.3 构建配置

文件: ohos/build-profile.json5

{
  "apiType": "stageMode",
  "buildOption": {},
  "targets": [
    {
      "name": "default"
    }
  ]
}

apiType: "stageMode"表示使用Stage模型(鸿蒙推荐的应用模型)。

4.4 插件实现

文件: ohos/src/main/ets/components/plugin/FlutterFpsPlugin.ets

这是整个适配的核心文件,完整的实现如下:

import {
  FlutterPlugin,
  FlutterPluginBinding,
  MethodCall,
  MethodCallHandler,
  MethodChannel,
  MethodResult,
} from '@ohos/flutter_ohos';
import { displaySync } from '@kit.ArkGraphics2D';

/** FlutterFpsPlugin **/
export default class FlutterFpsPlugin implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;
  private backDisplaySync: displaySync.DisplaySync = displaySync.create();
  private passframeInfo: Array<displaySync.IntervalInfo> = []
  private fps: number = 0

  constructor() {
  }

  getUniqueClassName(): string {
    return "FlutterFpsPlugin"
  }

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.channel = new MethodChannel(binding.getBinaryMessenger(), "fps");
    this.channel.setMethodCallHandler(this)
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    if (this.channel != null) {
      this.channel.setMethodCallHandler(null)
    }
    this.backDisplaySync?.stop();
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
    if (call.method == "getPlatformVersion") {
      result.success("OpenHarmony ^ ^ ")
    } else if (call.method == "getRefreshRate") {
      let callback = (frameInfo: displaySync.IntervalInfo) => {
        this.passframeInfo.push(frameInfo);
        if (this.passframeInfo.length >= 2) {
          let tmp = this.passframeInfo[1].timestamp - this.passframeInfo[0].timestamp;
          let result = 1000000000 / Number.parseInt(tmp.toString());
          this.fps = result;
          this.passframeInfo.shift();
        }
      };
      this.backDisplaySync?.on("frame", callback);
      this.backDisplaySync?.start();
      result.success(this.fps)
    }
    else {
      result.notImplemented()
    }
  }
}

五、关键技术实现详解

5.1 Flutter插件接口实现

5.1.1 接口继承
export default class FlutterFpsPlugin implements FlutterPlugin, MethodCallHandler

插件需要实现两个核心接口:

  • FlutterPlugin: 插件生命周期管理
  • MethodCallHandler: 方法调用处理
5.1.2 插件生命周期
onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(binding.getBinaryMessenger(), "fps");
  this.channel.setMethodCallHandler(this)
}

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  if (this.channel != null) {
    this.channel.setMethodCallHandler(null)
  }
  this.backDisplaySync?.stop();
}
  • onAttachedToEngine: 插件附加到Flutter引擎时调用,创建MethodChannel并设置方法处理器
  • onDetachedFromEngine: 插件从Flutter引擎分离时调用,清理资源

注意: 必须在onDetachedFromEngine中调用backDisplaySync?.stop(),否则会导致资源泄漏。

5.2 FPS计算原理

5.2.1 HarmonyOS显示同步API

鸿蒙提供了displaySync API用于实现显示同步,核心类包括:

  • displaySync.DisplaySync: 显示同步器
  • displaySync.IntervalInfo: 帧间隔信息,包含时间戳
5.2.2 FPS计算算法

原插件的实现方式对比:

Android实现(直接获取刷新率):

WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
float refreshRate = windowManager.getDefaultDisplay().getRefreshRate();

iOS实现(使用CADisplayLink):

- (double)displayRefreshRate:(CADisplayLink *)link {
    if (@available(iOS 10.3, *)) {
        NSInteger preferredFPS = link.preferredFramesPerSecond;
        if (preferredFPS != 0) {
            return @(preferredFPS).doubleValue;
        }
        return @([UIScreen mainScreen].maximumFramesPerSecond).doubleValue;
    } else {
        return 60.0;
    }
}

HarmonyOS实现(通过帧间隔计算):

let callback = (frameInfo: displaySync.IntervalInfo) => {
  this.passframeInfo.push(frameInfo);
  if (this.passframeInfo.length >= 2) {
    let tmp = this.passframeInfo[1].timestamp - this.passframeInfo[0].timestamp;
    let result = 1000000000 / Number.parseInt(tmp.toString());
    this.fps = result;
    this.passframeInfo.shift();
  }
};
this.backDisplaySync?.on("frame", callback);
this.backDisplaySync?.start();
5.2.3 算法解析
  1. 收集帧信息: 使用队列passframeInfo存储最近的帧信息
  2. 计算时间差: 获取两帧之间的时间间隔(纳秒级精度)
  3. 计算FPS: FPS = 1,000,000,000 / 时间间隔纳秒
  4. 实时更新: 每次收到新帧时更新FPS值

为什么这样实现?

鸿蒙的displaySync API不直接提供刷新率,而是提供了帧间隔信息。因此需要通过计算两帧之间的时间间隔来推导FPS。这种方法的优点是:

  • 实时性更强,反映实际渲染性能
  • 不依赖系统静态配置
  • 可以检测到动态刷新率变化(如自适应刷新率)

5.3 平台差异处理

5.3.1 时间精度
  • Android: 毫秒级
  • iOS: 毫秒级
  • HarmonyOS: 纳秒级(9位时间戳)

因此HarmonyOS实现中使用1000000000(10亿)作为转换基数。

5.3.2 错误处理
this.backDisplaySync?.on("frame", callback);
this.backDisplaySync?.start();

使用可选链操作符?.确保在backDisplaySync为null时不会崩溃。

六、构建系统配置

6.1 包管理配置

文件: ohos/oh-package.json5

{
  "name": "flutter_fps",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "index.ets",
  "author": "",
  "license": "Apache-2.0",
  "dependencies": {}
}

这是鸿蒙的包管理文件,类似于npm的package.json

6.2 构建脚本

文件: ohos/hvigorfile.ts

import { harTasks } from '@ohos/hvigor-ohos-plugin';

export default {
    system: harTasks,
    plugins:[]
}

Hvigor是鸿蒙的构建工具,类似于Gradle。这里配置了HAR包的构建任务。

七、测试与验证

7.1 创建示例应用

example/ohos目录下创建鸿蒙示例应用,测试插件功能:

// example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:performance_fps/performance_fps.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double _fps = 0.0;

  
  void initState() {
    super.initState();
    _getFps();
  }

  Future<void> _getFps() async {
    double fps = await FpsPlugin.getRefreshRate;
    setState(() {
      _fps = fps;
    });
  }

  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('FPS Demo')),
        body: Center(
          child: Text('Current FPS: $_fps'),
        ),
      ),
    );
  }
}

7.2 兼容性测试

经过测试,本插件在以下环境中运行正常:

Flutter版本 HarmonyOS SDK DevEco Studio 测试结果
3.22.0-ohos 5.0.0(12) 5.1.0.828 ✓ 通过
3.35.8-ohos-0.0.2 6.0.1(21) 6.1.0 ✓ 通过

八、常见问题与解决方案

8.1 插件未找到

问题: 运行时报错"Plugin not found"

解决方案:

  1. 确认pubspec.yaml中正确配置了ohos平台
  2. 检查ohos/index.ets是否正确导出插件类
  3. 执行flutter clean后重新构建

8.2 MethodChannel调用失败

问题: 调用getRefreshRate时无响应

解决方案:

  1. 确认Channel名称一致(Dart层和原生层都应为"fps")
  2. 检查onMethodCall方法是否正确实现
  3. 确认setMethodCallHandler已调用

8.3 FPS计算不准确

问题: 返回的FPS值异常(如0或极大值)

解决方案:

  1. 确保passframeInfo队列长度至少为2
  2. 检查时间戳计算是否正确(纳秒级精度)
  3. 验证displaySync是否正常启动

8.4 资源泄漏

问题: 长时间运行后内存增长

解决方案:

  1. 确保onDetachedFromEngine中调用backDisplaySync?.stop()
  2. 检查是否正确清理MethodChannel处理器
  3. 使用DevEco Studio的Profiler工具监控内存

九、最佳实践

9.1 代码组织

  • 将平台相关代码放在ohos/src/main/ets/目录下
  • 使用清晰的命名规范,如FlutterFpsPlugin
  • 添加必要的注释说明关键逻辑

9.2 错误处理

try {
  // 插件逻辑
} catch (error) {
  result.error("PLUGIN_ERROR", error.message, null);
}

9.3 生命周期管理

onDetachedFromEngine(binding: FlutterPluginBinding): void {
  // 清理所有资源
  this.channel?.setMethodCallHandler(null);
  this.backDisplaySync?.stop();
  this.backDisplaySync = null;
  this.channel = null;
}

9.4 性能优化

  • 避免频繁创建和销毁对象
  • 使用对象池复用对象
  • 合理设置帧采样频率

十、总结与展望

10.1 核心要点回顾

  1. 架构设计: 遵循Flutter插件标准架构,保持Dart层API一致
  2. API映射: 将平台原生API映射到鸿蒙等效API
  3. 生命周期管理: 正确实现插件生命周期方法,避免资源泄漏
  4. 构建配置: 完善的鸿蒙构建系统配置
  5. 测试验证: 多版本兼容性测试

10.2 技术难点

  • API差异处理: 鸿蒙的displaySync与Android/iOS的刷新率获取API差异较大
  • 时间精度转换: 纳秒级时间戳的计算和转换
  • 构建系统迁移: 从Gradle/CocoaPods迁移到Hvigor

10.3 未来改进方向

  1. 自适应刷新率支持: 检测并支持动态刷新率变化
  2. 性能监控增强: 添加丢帧、卡顿等更多性能指标
  3. 可视化工具: 提供FPS实时监控UI组件
  4. 多平台统一: 进一步抽象平台差异,提供更统一的API

10.4 给开发者的建议

  1. 深入了解鸿蒙API: 熟悉@kit.ArkGraphics2D等核心Kit
  2. 参考官方文档: 查阅Flutter OHOS官方插件开发指南
  3. 充分测试: 在不同设备和系统版本上进行测试
  4. 社区交流: 积极参与鸿蒙开发者社区,分享经验

十一、参考资料

结语

通过本文的详细介绍,相信读者已经掌握了Flutter插件适配鸿蒙平台的完整流程。随着鸿蒙生态的不断完善,越来越多的Flutter开发者将参与到鸿蒙应用开发中。希望本文能为开发者提供有价值的参考,推动Flutter在鸿蒙平台的发展。

让我们一起见证鸿蒙生态的繁荣!

Logo

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

更多推荐