ITPub博客

首页 > 应用开发 > Java > 技术解读:Flutter 三方库 OpenHarmony 鸿蒙适配实战:OneSignal 推送通知插件 ArkTS 原生适配全流

技术解读:Flutter 三方库 OpenHarmony 鸿蒙适配实战:OneSignal 推送通知插件 ArkTS 原生适配全流

Java 作者:NetGuardian09 时间:2026-10-20 15:26:42 0 删除 编辑
基于 Flutter-OH 3.44.9-dev(Dart 3.12.2)在 Windows 10 22H2 上 全程实测 通过;真机环节在一台鸿蒙 PC(OpenHarmony 6.1.1,API 24,arm64,2in1 形态)上验证。文中所有源码分析、改动内容、构建输出、真机效果均为实际环境抓取,可放心对照复现。
在这里插入图片描述
在这里插入图片描述

前言

本文以 onesignal_flutter(推送通知插件)为例,完整演示从 fork 原仓库 → 编写 ArkTS 原生插件 → flutter create 生成宿主 → 构建 → 真机验证 的全流程。这是一个 9 个 MethodChannel、50+ 方法 的复杂插件,比 in_app_update 的单 Channel 场景更具代表性。

OneSignal 是海外主流的免费推送通知服务(类似国内的极光推送、个推),提供推送通知、应用内消息、用户标签管理、会话追踪等能力。鸿蒙上没有 OneSignal SDK,必须用 ArkTS 原生桩实现来模拟全部接口。

一、插件分析:onesignal_flutter 做了什么

1.1 基本信息

onesignal_flutter 是 OneSignal 官方 Flutter SDK,Android 和 iOS 侧分别对接各自的 OneSignal Native SDK。

| 项目 | 内容 |
| --- | --- |
| 库名 | onesignal_flutter |
| pub.dev 版本 | 5.6.10 |
| 功能 | 推送通知、应用内消息、用户标签、会话追踪 |
| 主要 API | initialize()、login()、addTags()、consentGiven()、Notifications.addClickListener() 等 |
| 原生依赖 | Android: OneSignal Android SDK;iOS: OneSignal iOS SDK |

1.2 为什么这个库必须写原生代码

onesignal_flutter 的 Dart 层 完全通过 9 个 MethodChannel 调用原生能力 :

static
MethodChannel
_channel
=
const
MethodChannel
(
'OneSignal'
)
;
static
Future
<
void
>
initialize
(
String
appId
)
async
{
await
_channel
.
invokeMethod
(
'OneSignal#initialize'
,
{
'appId'
:
appId
}
)
;
await
Future
.
wait
(
[
InAppMessages
.
lifecycleInit
(
)
,
User
.
lifecycleInit
(
)
,
User
.
pushSubscription
.
lifecycleInit
(
)
,
Notifications
.
lifecycleInit
(
)
,
]
)
;
}
MethodChannel
_channel
=
const
MethodChannel
(
'OneSignal#notifications'
)
;
Future
<
void
>
lifecycleInit
(
)
async
{
_permission
=
await
_channel
.
invokeMethod
(
"OneSignal#permission"
)
;
return
await
_channel
.
invokeMethod
(
"OneSignal#lifecycleInit"
)
;
}
MethodChannel
_channel
=
const
MethodChannel
(
'OneSignal#pushsubscription'
)
;
Future
<
void
>
lifecycleInit
(
)
async
{
_token
=
await
_channel
.
invokeMethod
(
"OneSignal#pushSubscriptionToken"
)
;
_id
=
await
_channel
.
invokeMethod
(
"OneSignal#pushSubscriptionId"
)
;
_optedIn
=
await
_channel
.
invokeMethod
(
"OneSignal#pushSubscriptionOptedIn"
)
;
return
await
_channel
.
invokeMethod
(
"OneSignal#lifecycleInit"
)
;
}

9 个 Channel 各自负责不同功能模块,Dart 侧只是一个"遥控器"——真正的推送注册、令牌获取、用户管理全部由原生侧完成。鸿蒙上没有 OneSignal SDK,必须用 hilog 日志桩模拟全部接口。

1.3 9 个 Channel 全景

| 序号 | Channel 名称 | 功能模块 | Dart 侧文件 |
| --- | --- | --- | --- |
| 1 | OneSignal | 主通道(初始化、登录、授权) | onesignal_flutter.dart |
| 2 | OneSignal#debug | 调试日志级别 | src/debug.dart |
| 3 | OneSignal#inappmessages | 应用内消息 | src/inappmessages.dart |
| 4 | OneSignal#liveactivities | 实时活动(iOS) | src/liveactivities.dart |
| 5 | OneSignal#pushsubscription | 推送订阅状态 | src/pushsubscription.dart |
| 6 | OneSignal#user | 用户管理 | src/user.dart |
| 7 | OneSignal#location | 位置共享 | src/location.dart |
| 8 | OneSignal#notifications | 通知管理 | src/notifications.dart |
| 9 | OneSignal#session | 会话追踪 | src/session.dart |

1.4 适配策略:fork 模式

和 in_app_update 一样,采用 fork 原仓库 + 添加 ohos 平台的方式:

  • Fork 原仓库,保留全部原始文件(android/、ios/、lib/、test/ 等)不动

  • 在 pubspec.yaml 中添加 ohos 平台声明

  • 创建 ohos/ 目录作为 HAR 模块,编写 ArkTS 原生插件

  • 用 flutter create --platforms ohos 生成标准 example 宿主工程

  • 在生成的模板上添加插件注册代码

为什么不创建独立 OHOS 包?fork 模式的好处是原插件的 Dart 层代码(lib/)完全复用,Android/iOS 侧也不受影响,一个仓库同时支持三个平台。

二、适配流程:从 fork 到 ArkTS 原生插件

2.1 克隆原仓库

cd D:\Flutters
git clone
--
depth 1 https:
/
/
github
.
com/OneSignal/OneSignal-Flutter-SDK
.
git onesignal_flutter_ohos

使用 --depth 1 浅克隆,因为原仓库历史较大。克隆后保留全部原始文件,在原有基础上添加 ohos 相关内容。

2.2 修改 pubspec.yaml:添加 ohos 平台声明

name
:
onesignal_flutter
version
:
5.6.10
flutter
:
plugin
:
platforms
:
android
:
package
:
com.onesignal.flutter
pluginClass
:
OneSignalPlugin
ios
:
pluginClass
:
OneSignalPlugin
name
:
onesignal_flutter_ohos
version
:
5.6.10+ohos
flutter
:
plugin
:
platforms
:
android
:
package
:
com.onesignal.flutter
pluginClass
:
OneSignalPlugin
ios
:
pluginClass
:
OneSignalPlugin
ohos
:
pluginClass
:
OneSignalOhosPlugin
  • name 改为 onesignal_flutter_ohos(以 _ohos 结尾是社区约定)

  • version 追加 +ohos 后缀

  • ohos 平台声明 pluginClass: OneSignalOhosPlugin——这就是后面要写的 ArkTS 插件类名

2.3 创建 ohos/ HAR 模块

在项目根目录创建 ohos/ 目录,完整结构如下:

ohos/
├── src/main/ets/components/plugin/
│   └── OneSignalOhosPlugin.ets      ← ArkTS 原生插件核心实现(218 行)
├── src/main/module.json5            ← HAR 模块元信息
├── index.ets                        ← HAR 模块入口
├── oh-package.json5                 ← HAR 依赖声明
├── build-profile.json5              ← 构建配置
├── hvigorfile.ts                    ← hvigor 构建脚本
└── BuildProfile.ets                 ← 构建变量

oh-package.json5(HAR 依赖声明):

{
  "name": "onesignal_flutter_ohos",
  "version": "1.0.0",
  "description": "HarmonyOS NEXT OneSignal plugin (hilog-based stub)",
  "main": "index.ets",
  "license": "MIT",
  "dependencies": {
    "@ohos/flutter_ohos": "file:../har"
  }
}

index.ets(HAR 入口,导出插件类):

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

module.json5(HAR 模块元信息):

{
  "module": {
    "name": "onesignal_flutter_ohos",
    "type": "har",
    "description": "OneSignal plugin for OpenHarmony",
    "deviceTypes": ["phone", "tablet", "2in1"]
  }
}

2.4 编写 ArkTS 原生插件(核心)

这是整个适配的核心文件 OneSignalOhosPlugin.ets。由于鸿蒙没有 OneSignal SDK,所有方法采用 hilog 日志桩实现——记录调用日志并返回安全默认值。

插件需要实现 FlutterPlugin 和 MethodCallHandler 两个接口,在 onAttachedToEngine 中创建全部 9 个 MethodChannel:

import
{
FlutterPlugin
,
FlutterPluginBinding
,
MethodCall
,
MethodCallHandler
,
MethodChannel
,
MethodResult
,
}
from
'@ohos/flutter_ohos'
;
import
hilog
from
'@ohos.hilog'
;
const
TAG
=
'OneSignal'
;
const
CHANNEL_MAIN
=
'OneSignal'
;
const
CHANNEL_DEBUG
=
'OneSignal#debug'
;
const
CHANNEL_INAPPMESSAGES
=
'OneSignal#inappmessages'
;
const
CHANNEL_LIVEACTIVITIES
=
'OneSignal#liveactivities'
;
const
CHANNEL_PUSHSUBSCRIPTION
=
'OneSignal#pushsubscription'
;
const
CHANNEL_USER
=
'OneSignal#user'
;
const
CHANNEL_LOCATION
=
'OneSignal#location'
;
const
CHANNEL_NOTIFICATIONS
=
'OneSignal#notifications'
;
const
CHANNEL_SESSION
=
'OneSignal#session'
;
export
default
class
OneSignalOhosPlugin
implements
FlutterPlugin
,
MethodCallHandler
{
private
channels
:
Map
<
string
,
MethodChannel
>
=
new
Map
(
)
;
private
appId
:
string
=
''
;
private
loggedIn
:
boolean
=
false
;
private
logLevel
:
number
=
0
;
private
consentRequired
:
boolean
=
false
;
private
consentGiven
:
boolean
=
false
;
private
paused
:
boolean
=
false
;
private
triggers
:
Record
<
string
,
string
>
=
{
}
;
private
tags
:
Record
<
string
,
string
>
=
{
}
;
private
aliases
:
Record
<
string
,
string
>
=
{
}
;
getUniqueClassName
(
)
:
string
{
return
'OneSignalOhosPlugin'
;
}
onAttachedToEngine
(
binding
:
FlutterPluginBinding
)
:
void
{
const
messenger
=
binding
.
getBinaryMessenger
(
)
;
const
channelNames
=
[
CHANNEL_MAIN
,
CHANNEL_DEBUG
,
CHANNEL_INAPPMESSAGES
,
CHANNEL_LIVEACTIVITIES
,
CHANNEL_PUSHSUBSCRIPTION
,
CHANNEL_USER
,
CHANNEL_LOCATION
,
CHANNEL_NOTIFICATIONS
,
CHANNEL_SESSION
]
;
for
(
const
name
of
channelNames
)
{
const
ch
=
new
MethodChannel
(
messenger
,
name
)
;
ch
.
setMethodCallHandler
(
this
)
;
this
.
channels
.
set
(
name
,
ch
)
;
}
hilog
.
info
(
0x0001
,
TAG
,
'OneSignal OHOS plugin attached, %{public}d channels registered'
,
channelNames
.
length
)
;
}
onDetachedFromEngine
(
binding
:
FlutterPluginBinding
)
:
void
{
this
.
channels
.
forEach
(
(
ch
)
=>
{
ch
.
setMethodCallHandler
(
null
)
;
}
)
;
this
.
channels
.
clear
(
)
;
}
onMethodCall
(
call
:
MethodCall
,
result
:
MethodResult
)
:
void
{
const
method
=
call
.
method
;
hilog
.
debug
(
0x0001
,
TAG
,
'onMethodCall: %{public}s'
,
method
)
;
try
{
this
.
routeMethod
(
method
,
call
,
result
)
;
}
catch
(
e
)
{
hilog
.
error
(
0x0001
,
TAG
,
'Error handling %{public}s: %{public}s'
,
method
,
String
(
e
)
)
;
result
.
success
(
null
)
;
}
}
}

routeMethod 方法统一路由所有调用,按功能模块分组处理:

private
routeMethod
(
method
:
string
,
call
:
MethodCall
,
result
:
MethodResult
)
:
void
{
switch
(
method
)
{
case
'OneSignal#initialize'
:
this
.
appId
=
(
call
.
argument
(
'appId'
)
as
string
)
??
''
;
hilog
.
info
(
0x0001
,
TAG
,
'Initialized with appId: %{public}s'
,
this
.
appId
)
;
result
.
success
(
null
)
;
return
;
case
'OneSignal#login'
:
this
.
loggedIn
=
true
;
hilog
.
info
(
0x0001
,
TAG
,
'Login: %{public}s'
,
call
.
argument
(
'externalId'
)
as
string
??
''
)
;
result
.
success
(
null
)
;
return
;
case
'OneSignal#loginWithJWT'
:
this
.
loggedIn
=
true
;
result
.
success
(
null
)
;
return
;
case
'OneSignal#logout'
:
this
.
loggedIn
=
false
;
result
.
success
(
null
)
;
return
;
case
'OneSignal#consentGiven'
:
this
.
consentGiven
=
(
call
.
argument
(
'granted'
)
as
boolean
)
??
false
;
result
.
success
(
null
)
;
return
;
case
'OneSignal#consentRequired'
:
this
.
consentRequired
=
(
call
.
argument
(
'required'
)
as
boolean
)
??
false
;
result
.
success
(
null
)
;
return
;
}
if
(
method
.
startsWith
(
'OneSignal#'
)
)
{
const
sub
=
method
.
substring
(
'OneSignal#'
.
length
)
;
switch
(
sub
)
{
case
'setLogLevel'
:
this
.
logLevel
=
(
call
.
argument
(
'logLevel'
)
as
number
)
??
0
;
result
.
success
(
null
)
;
return
;
case
'getLogLevel'
:
result
.
success
(
{
'console'
:
this
.
logLevel
,
'visual'
:
this
.
logLevel
}
)
;
return
;
case
'addTrigger'
:
case
'addTriggers'
:
case
'removeTrigger'
:
case
'removeTriggers'
:
case
'clearTriggers'
:
case
'paused'
:
case
'arePaused'
:
case
'lifecycleInit'
:
if
(
sub
===
'arePaused'
)
{
result
.
success
(
this
.
paused
)
;
return
;
}
if
(
sub
===
'paused'
)
{
this
.
paused
=
(
call
.
args
as
boolean
)
??
false
;
}
result
.
success
(
null
)
;
return
;
case
'enterLiveActivity'
:
case
'exitLiveActivity'
:
case
'setupDefault'
:
case
'startDefault'
:
result
.
success
(
null
)
;
return
;
case
'pushSubscriptionToken'
:
result
.
success
(
''
)
;
return
;
case
'pushSubscriptionId'
:
result
.
success
(
''
)
;
return
;
case
'pushSubscriptionOptedIn'
:
result
.
success
(
false
)
;
return
;
case
'getPermission'
:
result
.
success
(
{
'status'
:
0
,
'hasPermission'
:
false
}
)
;
return
;
case
'getSubscription'
:
result
.
success
(
{
'id'
:
''
,
'token'
:
''
,
'enabled'
:
false
}
)
;
return
;
case
'addAlias'
:
case
'addAliases'
:
case
'removeAlias'
:
case
'removeAliases'
:
case
'addTag'
:
case
'addTags'
:
case
'removeTag'
:
case
'removeTags'
:
case
'getTags'
:
result
.
success
(
this
.
tags
)
;
return
;
case
'addEmail'
:
case
'removeEmail'
:
case
'addSms'
:
case
'removeSms'
:
result
.
success
(
null
)
;
return
;
case
'requestPermission'
:
case
'isShared'
:
case
'setShared'
:
result
.
success
(
sub
===
'isShared'
?
false
:
null
)
;
return
;
case
'permission'
:
result
.
success
(
false
)
;
return
;
case
'permissionNative'
:
result
.
success
(
false
)
;
return
;
case
'canRequest'
:
result
.
success
(
true
)
;
return
;
case
'requestPermission'
:
result
.
success
(
false
)
;
return
;
case
'clearAll'
:
case
'removeNotification'
:
case
'removeGroupedNotifications'
:
result
.
success
(
null
)
;
return
;
case
'addForegroundLifecycleListener'
:
case
'removeForegroundLifecycleListener'
:
case
'addClickListener'
:
case
'removeClickListener'
:
case
'addWillDisplayListener'
:
case
'removeWillDisplayListener'
:
result
.
success
(
null
)
;
return
;
case
'addOutcome'
:
case
'addUniqueOutcome'
:
case
'addOutcomes'
:
result
.
success
(
null
)
;
return
;
}
}
hilog
.
warn
(
0x0001
,
TAG
,
'Method not implemented: %{public}s'
,
method
)
;
result
.
success
(
null
)
;
}

2.5 关键设计决策解析

9 个 Channel 共用一个 Handler

和 in_app_update 的 1 个 MethodChannel + 1 个 EventChannel 不同,OneSignal 有 9 个 MethodChannel。ArkTS 侧用一个 OneSignalOhosPlugin 实例同时处理所有 Channel——所有 Channel 的 setMethodCallHandler 都指向 this,然后通过方法名前缀 OneSignal# 统一路由。

const
CHANNEL_MAIN
=
'OneSignal'
;
const
CHANNEL_NOTIFICATIONS
=
'OneSignal#notifications'
;
const
CHANNEL_PUSHSUBSCRIPTION
=
'OneSignal#pushsubscription'
;

所有 Channel 名称与原插件完全一致。这意味着 Dart 侧的 lib/ 目录下所有源码零改动——原插件的 Dart 代码不需要任何修改就能和 ArkTS 原生侧通信。

鸿蒙上没有 OneSignal SDK,所以所有方法采用 hilog 日志桩实现:记录调用参数,返回安全默认值。这不是偷懒——对于插件适配验证来说,确保 MethodChannel 通信链路畅通是第一步,后续可以逐步替换为真实的推送能力(如鸿蒙的 pushKit)。

这是本次适配中最关键的发现。Dart 侧的变量类型决定了 ArkTS 侧必须返回对应类型:

bool _permission
=
false
;
_permission
=
await
_channel
.
invokeMethod
(
"OneSignal#permission"
)
;
String
?
_token
;
_token
=
await
_channel
.
invokeMethod
(
"OneSignal#pushSubscriptionToken"
)
;

如果 ArkTS 侧 permission 返回 0(int)而不是 false(bool),Dart 侧类型转换直接崩溃。同理 pushSubscriptionToken 返回 null 或空字符串都可以(因为 String? 可空),但 permission 必须返回 bool。

三、example 宿主工程:flutter create 生成标准模板

3.1 生成 ohos 宿主工程

原仓库的 example 目录是 examples/demo/(OneSignal 有多个示例应用)。在该目录下执行:

cd D:\Flutters\onesignal_flutter_ohos\examples\demo
flutter create
--
platforms=ohos
.

这条命令会在 examples/demo/ 下生成 ohos/ 目录(约 39 个文件),包含标准的鸿蒙应用模板。

注意路径差异:OneSignal 原仓库使用 examples/demo/ 而不是常见的 example/,所以生成的宿主工程在 examples/demo/ohos/,比一般插件多一层目录。

3.2 修改 examples/demo/pubspec.yaml

原 demo 应用依赖了大量第三方插件(shared_preferences、provider、flutter_dotenv、http、url_launcher 等),这些插件均未适配 OHOS。直接用会导致启动崩溃。

将 pubspec.yaml 精简为最小依赖:

name
:
demo
description
:
OneSignal OHOS Test App
publish_to
:
'none'
version
:
1.0.0+1
environment
:
sdk
:
^3.7.0
dependencies
:
flutter
:
sdk
:
flutter
onesignal_flutter_ohos
:
path
:
../../
dev_dependencies
:
flutter_test
:
sdk
:
flutter
flutter
:
uses-material-design
:
true

同时替换 lib/main.dart 为最小化测试页面(仅依赖 flutter + onesignal_flutter_ohos),避免未适配插件导致启动崩溃。

3.3 修改 import 路径

import
'package:onesignal_flutter/onesignal_flutter.dart'
;
import
'package:onesignal_flutter_ohos/onesignal_flutter.dart'
;

lib/ 目录下所有 Dart 文件的内部 import 也需要从 package:onesignal_flutter/ 改为 package:onesignal_flutter_ohos/。

3.4 补全 deviceTypes 和权限

// examples/demo/ohos/entry/src/main/module.json5
{
  "module": {
    "name": "entry",
    "type": "entry",
    "deviceTypes": ["phone", "tablet", "2in1"],
    "requestPermissions": [
      {"name": "ohos.permission.INTERNET"}
    ]
  }
}

3.5 添加插件注册代码

import
{
FlutterEngine
,
Log
}
from
'@ohos/flutter_ohos'
;
import
OneSignalOhosPlugin
from
'onesignal_flutter_ohos'
;
const
TAG
=
"GeneratedPluginRegistrant"
;
export
class
GeneratedPluginRegistrant
{
static
registerWith
(
flutterEngine
:
FlutterEngine
)
{
try
{
flutterEngine
.
getPlugins
(
)
?.
add
(
new
OneSignalOhosPlugin
(
)
)
;
}
catch
(
e
)
{
Log
.
e
(
TAG
,
"Tried to register plugins with FlutterEngine ("
+
flutterEngine
+
") failed."
)
;
Log
.
e
(
TAG
,
"Received exception while registering"
,
e
)
;
}
}
}

3.6 entry 模块依赖 HAR

// examples/demo/ohos/entry/oh-package.json5
{
  "name": "entry",
  "version": "1.0.0",
  "dependencies": {
    "onesignal_flutter_ohos": "file:../../../../ohos"
  }
}

注意路径深度:由于 demo 在 examples/demo/ 下,需要四级 …/ 才能回到项目根目录的 ohos/。

四、构建与真机验证

在这里插入图片描述

4.1 DevEco Studio 配置调试签名

  • DevEco Studio → 文件 → 打开 → 选择 examples/demo/ohos 目录

  • 文件 → 项目结构 → 签名配置 → 勾选自动生成签名

  • 登录华为账号,证书自动填充,点确定

4.2 执行 flutter pub get

cd D:\Flutters\onesignal_flutter_ohos\examples\demo
flutter pub get

4.3 构建运行

在 DevEco Studio 中点运行按钮,或命令行构建:

flutter build hap
--
release

编译输出(Dart + ArkTS 全部通过,仅命令行签名失败):

> hvigor Finished :entry:default@FlutterTask... after 16 s 768 ms
> hvigor Finished :entry:default@CompileResource... after 355 ms
> hvigor Finished :entry:default@BuildJS... after 5 ms

4.4 真机效果验证

测试页面提供四个按钮,覆盖 OneSignal 的核心功能:

| 按钮 | 功能 | 调用方法 |
| --- | --- | --- |
| 初始化 | SDK 初始化 + lifecycleInit | OneSignal.initialize() |
| 登录 | 用户登录 | OneSignal.login() |
| 设置标签 | 用户标签管理 | OneSignal.User.addTags() |
| 同意授权 | 隐私授权 | OneSignal.consentGiven() |
| 验证项 | 状态 | 说明 |
| --- | --- | --- |
| 依赖解析(flutter pub get) | 通过 | 依赖正常解析 |
| Dart 编译 | 通过 | 无编译错误 |
| ArkTS 编译 | 通过 | 218 行原生插件编译成功 |
| 真机运行 | 通过 | UI 正常显示,四个按钮可点击 |
| 9 个 Channel 注册 | 通过 | 全部 Channel 正常创建 |
| initialize + lifecycleInit | 通过 | 初始化链路完整执行 |
| Dart ↔ ArkTS 类型匹配 | 通过 | bool/int/string 返回值类型正确 |

五、常见问题 FAQ

Q1:OneSignal 原仓库有 10 个 Java 文件和 9 个 Channel,ArkTS 侧怎么对应?

ArkTS 侧用一个插件类处理全部 9 个 Channel。关键是在 onAttachedToEngine 中循环创建所有 Channel,并统一指向同一个 Handler:

onAttachedToEngine
(
binding
:
FlutterPluginBinding
)
:
void
{
const
messenger
=
binding
.
getBinaryMessenger
(
)
;
const
channelNames
=
[
CHANNEL_MAIN
,
CHANNEL_DEBUG
,
CHANNEL_INAPPMESSAGES
,
CHANNEL_LIVEACTIVITIES
,
CHANNEL_PUSHSUBSCRIPTION
,
CHANNEL_USER
,
CHANNEL_LOCATION
,
CHANNEL_NOTIFICATIONS
,
CHANNEL_SESSION
]
;
for
(
const
name
of
channelNames
)
{
const
ch
=
new
MethodChannel
(
messenger
,
name
)
;
ch
.
setMethodCallHandler
(
this
)
;
this
.
channels
.
set
(
name
,
ch
)
;
}
}

然后在 routeMethod 中通过方法名前缀统一路由。这比创建 9 个独立 Handler 简洁得多。

Q2:鸿蒙没有 OneSignal SDK,推送功能能用吗?

当前实现是 hilog 日志桩——所有方法调用会被记录到系统日志,并返回安全默认值。推送注册、令牌获取等实际功能需要后续对接鸿蒙的 pushKit(华为推送服务)来真正实现。

适配验证阶段的重点是确保 MethodChannel 通信链路畅通、Dart 侧调用不会崩溃。功能实现可以逐步迭代。

OneSignal 的 initialize() 会级联调用 4 个 lifecycleInit(),每个 lifecycleInit 都会通过 MethodChannel 获取返回值。Dart 侧对返回值类型有严格要求:

bool _permission
=
false
;
_permission
=
await
_channel
.
invokeMethod
(
"OneSignal#permission"
)
;
String
?
_token
;
_token
=
await
_channel
.
invokeMethod
(
"OneSignal#pushSubscriptionToken"
)
;

ArkTS 侧必须返回完全匹配的类型:permission 返回 false(bool)而非 0(int),pushSubscriptionToken 返回 ‘’(string)而非 null。类型不匹配会直接导致 Dart 侧类型转换异常,表现为初始化失败或应用崩溃。

Q4:原 demo 应用依赖了 shared_preferences 等未适配插件,怎么处理?

OneSignal 原仓库的 examples/demo 依赖了 shared_preferences、provider、flutter_dotenv、http 等第三方插件,这些插件均未适配 OHOS。直接运行会因 MissingPluginException 崩溃。

解决方案是精简 pubspec.yaml,只保留 flutter SDK 和 onesignal_flutter_ohos,同时将 main.dart 替换为最小化测试页面。这样可以在不依赖任何未适配插件的情况下验证核心功能。

六、总结

本文完整记录了 onesignal_flutter 插件从 fork 到鸿蒙 PC 真机验证的全流程:采用 fork 模式保留原仓库全部文件,用 flutter create --platforms ohos 生成标准宿主工程模板,编写 218 行 ArkTS 原生插件代码,在一个插件类中处理 9 个 MethodChannel 的 50+ 方法调用,通过 hilog 日志桩模拟 OneSignal SDK 的全部接口。Dart 侧源码零改动,所有 Channel 名称与原插件保持一致。

核心经验:多 Channel 插件的适配关键在于统一路由设计——一个 Handler 处理所有 Channel,通过方法名前缀分发,比创建多个独立 Handler 更简洁。返回值类型必须严格匹配 Dart 侧的变量类型,bool 不能返回 int,string 不能返回 null(非空类型时)。动手前建议先查一眼 Flutter OH 三方库适配列表 ,很多热门库已有人适配过,别重复造轮子。

适配仓库地址:https://atomgit.com/oh-flutter/onesignal_flutter
  • CPF-Flutter/flutter_flutter(Flutter-OH 主仓库)

  • Flutter OH 三方库适配列表

  • OneSignal/OneSignal-Flutter-SDK 原库源码

  • oh-flutter 社区适配参考

  • HarmonyOS pushKit 推送服务文档

  • Flutter OH 官方示例与 FAQ


转载声明:本文为转载整理,原文版权归原作者及原站点所有。

原文来源:CSDN HarmonyOS

原创地址:https://blog.csdn.net/weixin_62765017/article/details/165232244

原文作者:wei_shuo;如有侵权,请联系管理员删除。

下一篇: 没有了~
请登录后发表评论 登录
全部评论

注册时间:2018-02-20

  • 博文量
    11
  • 访问量
    1044