Flutter/OpenHarmony 三方库适配实战:jpush_flutter 推送兼容层

一、项目信息

项目内容
仓库名称jpush-flutter-plugin
Dart 包名jpush_flutter
AtomGit 仓库地址https://atomgit.com/oh-flutter/jpush-flutter-plugin
上游适配版本2.1.4
OHOS 适配版本2.1.4-ohos.1
方法通道jpush
OHOS 原生入口JPushPlugin
示例包名com.example.jpush_example
HAP 产物example/build/ohos/hap/entry-default-unsigned.hap
当前状态已完成分析、测试、构建和模拟器启动验证,并已推送到 main

在这里插入图片描述

二、这个库为什么需要 OHOS 适配

JPush 的 Dart 层并没有直接依赖 Android 类,它把业务操作集中到一条
MethodChannel('jpush') 上:

static final JPush _instance =
    JPush.private(const MethodChannel('jpush'), const LocalPlatform());

这条设计让适配边界变得很清楚:

业务页面
   |
   | JPush Dart API
   v
MethodChannel("jpush")
   |
   +--> Android JPush SDK
   +--> iOS JPush SDK
   +--> OHOS JPushPlugin(本次新增)

真正困难的不是创建 MethodChannel,而是 OpenHarmony 当前没有可验证的
JPush 原生 SDK。如果把 setup、getRegistrationID 或 setTags
直接返回成功,业务会误以为设备已经注册并能收到远端推送,这比明确返回
不支持更容易造成线上问题。

因此本次适配分成两层:

  1. 工程层完成 OHOS 插件发现、HAR 构建、插件注册和通道存活。
  2. 能力层只实现可验证的平台版本与适配版本查询,其他远端推送能力返回
    jpush_unsupported。

三、先让 Flutter OHOS 找到插件

根 pubspec.yaml 保留 Android 和 iOS 声明,只增加 OHOS 入口,并把版本
标记为 OHOS 适配版本:

version: 2.1.4-ohos.1

environment:
  sdk: ">=3.2.0 <4.0.0"

flutter:
  plugin:
    platforms:
      android:
        package: com.jiguang.jpush
        pluginClass: JPushPlugin
      ios:
        pluginClass: JPushPlugin
      ohos:
        pluginClass: JPushPlugin

这里有几个容易被忽略的点:

  • pluginClass 必须和 index.ets 最终导出的默认类一致。
  • Dart SDK 下限从旧工程的过低版本提升到 3.2.0,才能与当前 Flutter OH
    工具链一致。
  • platform 依赖解析到 3.2.0,示例代码中的过时 API 同步更新。
  • 版本追加 -ohos.1,避免与上游原版本混淆。

OHOS 模块是 HAR,不是应用工程,所以它的 hvigorfile.ts 使用
harTasks:

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

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

index.ets 负责把 ArkTS 插件类交给 Flutter 生成的注册器:

import JPushPlugin
  from './src/main/ets/components/plugin/JPushPlugin';

export default JPushPlugin;

如果这一层缺少任何一环,都会出现类似问题:

缺失位置典型现象
pubspec.yaml 无 ohosFlutter 不生成 OHOS 插件注册代码
hvigorfile.ts 使用 appTasksHAR 构建任务类型错误
index.ets 没有默认导出ArkTS 注册器找不到插件类
HAR 模块名与依赖不一致ohpm 依赖解析失败
通道名不一致Dart 调用抛出 MissingPluginException

四、ArkTS 插件的最小完整实现

JPushPlugin.ets 保留和 Dart 完全一致的通道名:

const CHANNEL = 'jpush';
const UNSUPPORTED = 'jpush_unsupported';

export default class JPushPlugin
  implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;

  getUniqueClassName(): string {
    return 'JPushPlugin';
  }

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

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

方法分发只返回两类结果:

onMethodCall(call: MethodCall, result: MethodResult): void {
  switch (call.method) {
    case 'getPlatformVersion':
      result.success('OHOS');
      return;
    case 'getSDKVersion':
      result.success(
        'jpush_flutter OHOS adapter 2.1.4-ohos.1',
      );
      return;
    default:
      result.error(
        UNSUPPORTED,
        `${call.method}: JPush SDK is not available on OpenHarmony.`,
        null,
      );
      return;
  }
}

Dart 侧新增两个轻量查询方法,示例和业务都能验证插件是否真正注册:

Future<String> getPlatformVersion() async {
  final String? version =
      await _channel.invokeMethod<String>('getPlatformVersion');
  return version ?? 'Unknown';
}

Future<String> getSDKVersion() async {
  final String? version =
      await _channel.invokeMethod<String>('getSDKVersion');
  return version ?? 'Unknown';
}

这段 ArkTS 分发的核心价值是让失败可诊断:

  • result.success 只用于已经验证的平台能力。
  • result.error 携带稳定错误码,Dart 可以精准判断 OHOS 尚未接入。
  • onDetachedFromEngine 清理 handler,避免引擎重建后残留旧通道。
  • 不实现假的 Registration ID,也不返回伪造 Tag 列表。

五、Dart 层保持原调用方式

上游 JPush 继续使用静态单例,业务代码不需要为 OHOS 写另一套 API:

final JPush jpush = JPush();

final String platform = await jpush.getPlatformVersion();
debugPrint(platform); // OHOS

远端推送方法仍按上游方式调用,但 OHOS 会返回 PlatformException:

try {
  final Map<dynamic, dynamic> tags = await jpush.setTags([
    'openharmony',
    'flutter',
  ]);
  debugPrint('$tags');
} on PlatformException catch (error) {
  if (error.code == 'jpush_unsupported') {
    debugPrint('OHOS 尚未接入 JPush 原生 SDK');
  } else {
    rethrow;
  }
}

这里刻意没有修改 Dart API 的签名。未来接入真实 OHOS SDK 时,只需要替换
ArkTS 分支,业务层不用迁移。

六、同步修复 Dart 和示例工程

适配过程中同时处理了几处会阻断当前工具链的问题:

  1. LocalNotification 构造参数从过时的 @required 改为 required。
  2. 示例中的 MaterialStateProperty 更新为 WidgetStateProperty。
  3. 清理示例未使用的 mockito、test 和 jmessage_flutter。
  4. 替换过时测试 API,使用
    TestDefaultBinaryMessengerBinding 注册 Mock 通道。
  5. 使用当前 Flutter OH 工具链生成 example/ohos。
  6. 增加 getPlatformVersion 和 getSDKVersion 公开方法,并让示例首屏
    显示真实注册结果。

根测试验证通道契约:

test('OHOS adapter exposes the jpush channel contract', () async {
  expect(
    await channel.invokeMethod<String>('getPlatformVersion'),
    'OHOS',
  );
  expect(
    await channel.invokeMethod<String>('getSDKVersion'),
    contains('2.1.4-ohos.1'),
  );
});

这类测试不能验证远端推送,但可以阻止通道名、版本号和插件分发被后续改动
意外破坏。

七、示例页面验证插件注册

示例保留上游的推送操作按钮,并通过状态区域展示平台调用结果:

final String version = await jpush.getPlatformVersion();
setState(() {
  debugLabel = 'OHOS plugin: $version';
});

图 1:OpenHarmony 模拟器上的 jpush_flutter 示例首屏。页面已加载,插件
通道和示例按钮均可访问。

截图中的状态区显示插件注册结果,通知、Tag、Alias 和角标按钮仍然保留,
是因为它们展示上游能力边界,并不代表这些业务已经在 OHOS 上接通。点击后应看到稳定、可解释的
jpush_unsupported,而不是静默成功。

八、HAP 构建与测试

本次实际执行的核心命令:

flutter analyze
flutter test
cd example
flutter analyze
flutter test
flutter build hap --debug --no-codesign

验证结果:

检查项结果
根包 analyze通过,No issues found
根包测试通过
示例 analyze通过,No issues found
示例测试通过
HAP 构建通过
产物entry-default-unsigned.hap

安装和启动命令:

hdc install -r \
  example/build/ohos/hap/entry-default-unsigned.hap
hdc shell aa start \
  -a EntryAbility \
  -b com.example.jpush_example

最终验证中的真实日志:

start ability successfully.
10-19 01:08:51.405 5205 5205 D ... Flutter:
FlutterNapi --> called onFirstFrame isPreload:0

进程 5205 在首帧后稳定存活,验证没有停留在“构建成功但启动即退出”。

九、常见问题与定位顺序

1. MissingPluginException

按以下顺序检查:

  1. pubspec.yaml 是否存在 ohos.pluginClass。
  2. example/ohos 的生成插件注册文件是否包含 JPushPlugin。
  3. index.ets 是否默认导出同一个类。
  4. ArkTS 和 Dart 的通道是否都叫 jpush。

2. HAP 能安装但启动失败

先卸载旧包,再安装当前构建。不同 Flutter Engine 和插件 HAR 混装可能在
插件注册阶段崩溃。验证日志必须检查 onFirstFrame,仅 aa start 返回
成功不足以证明首帧完成。

十、后续适配路线

  1. 接入或实现可验证的 JPush OHOS 原生 SDK。
  2. 实现 Registration ID、setup、Tag 和 Alias。
  3. 接入通知授权、本地通知和通知点击事件。
  4. 增加 OHOS 通知事件的 EventChannel 或方法回传。

十一、目录结构

jpush-flutter-plugin_ohos/
├── lib/jpush_flutter.dart
├── ohos/
│   ├── build-profile.json5
│   ├── hvigorfile.ts
│   ├── index.ets
│   └── src/main/ets/components/plugin/JPushPlugin.ets
├── example/
│   ├── lib/main.dart
│   └── ohos/
├── test/jpush_flutter_test.dart
├── photos/jpush_ohos.jpeg
├── verification.md
├── CHANGELOG.md
└── README.md

十二、总结

这次适配没有把“能编译”当作最终结果,而是建立了完整证据链:

pubspec OHOS 声明
      |
      v
HAR 构建和 index.ets 导出
      |
      v
GeneratedPluginRegistrant 注册
      |
      v
MethodChannel("jpush") 分发
      |
      v
analyze + test + HAP build
      |
      v
hdc 安装 + aa start + onFirstFrame + 进程存活

对于没有原生 OHOS SDK 的推送能力,正确结果不是返回假成功,而是保留统一
API、给出稳定错误码,并把真实 SDK 接入列为明确的下一步。

AtomGit 目标仓库:https://atomgit.com/oh-flutter/jpush-flutter-plugin,当前状态:已推送到 main。

欢迎加入 CPF-Flutter 社区,一起交流
Flutter 三方库 OpenHarmony 适配、HAP 构建和模拟器/真机验证经验:
https://atomgit.com/CPF-Flutter

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐