18 KiB
极光推送使用说明
本文档说明当前项目中 jg-push 模块的设计、接入方式、常用 API、状态管理方式以及业务侧推荐用法。
适用范围:
HBuilderX原生App环境uni-app+Vue 3+Pinia- 当前项目目录:
src/sheep/utils/jg-push
目录结构
当前推送模块已经拆分为目录形式,便于长期维护:
src/sheep/utils/jg-push/
├── index.ts
├── service.ts
├── types.ts
└── README.md
职责说明:
index.ts:统一导出入口,对外保持稳定引用路径service.ts:极光推送工具类实现、事件监听、状态同步、官方 API 封装types.ts:对外暴露的类型声明、错误类型、原生插件接口定义README.md:使用说明文档
模块设计
当前模块采用“两层结构”:
jg-push工具层push-store状态层
对应文件:
- 工具层:
src/sheep/utils/jg-push/index.ts - 状态层:
src/sheep/stores/push-store.ts
这样设计的好处:
- 页面、组件、业务逻辑都只需要调用统一工具实例
- 推送运行状态由全局 store 统一维护
- 消息事件格式统一,降低业务接入成本
- 非
APP-PLUS环境下有兜底,不会直接调用原生插件报错
导入方式
默认导入单例:
import jgPush from "@@/utils/jg-push"
按需导入类或类型:
import jgPush, { JgPushService, type PushInitializeOptions, type JPushNotificationPayload, type PushStatusSnapshot } from "@@/utils/jg-push"
说明:
default export是单例对象,推荐业务侧直接使用JgPushService是类,一般不建议业务层重复 new- 所有类型都已通过
index.ts统一导出
运行前提
在使用本模块前,需要先确认原生端已经正确集成极光 HBuilderX 插件:
- 插件名称:
JG-JPush - 文档来源:
API.mdANDROID.mdIOS.md
如果原生插件未正确集成:
uni.requireNativePlugin("JG-JPush")将不可用- 工具类初始化会报出统一错误
- H5 环境下会自动走不支持分支,不会崩溃
状态管理说明
全局状态由 usePushStore() 维护。
核心状态包括:
- 当前运行平台
- 插件是否可用
- 是否已初始化
- 是否正在初始化
- 当前连接状态
- 推送是否被停止
- 通知权限状态
- 系统通知总开关状态
registrationId- 当前
alias - 当前
tags deviceToken- 最近一次通知/点击通知/自定义消息/应用内消息
- 最近一次错误
- 待消费跳转队列
- 轻量事件历史
读取方式示例:
import { usePushStore } from "@@/stores/push-store"
const pushStore = usePushStore()
console.log(pushStore.registrationId)
console.log(pushStore.alias)
console.log(pushStore.tags)
console.log(pushStore.connected)
console.log(pushStore.notificationPermission)
最小接入流程
推荐的接入顺序如下:
- 应用启动时初始化推送
- 登录成功后获取
registrationId并提交后端绑定 - 需要时监听通知事件
- 退出登录时先请求后端精确解绑,再清理本地 alias 和标签
1. 应用启动初始化
推荐在 App.vue 的 onLaunch 中尽早初始化。
示例:
import jgPush from "@@/utils/jg-push"
export default {
async onLaunch() {
try {
await jgPush.initialize({
debug: import.meta.env.DEV,
autoInitService: true,
enableCrashHandler: true,
requestAndroidPermission: true,
requestIOSNotificationPermission: true,
iosForegroundOptions: {
alert: true,
sound: true,
badge: true,
},
})
} catch (error) {
console.error("[JPush] 初始化失败", error)
}
},
}
说明:
debug:开发环境建议开启autoInitService:是否调用initJPushServiceenableCrashHandler:是否开启极光 Crash 收集requestAndroidPermission:Android 是否主动申请推送相关权限requestIOSNotificationPermission:iOS 是否主动查询或申请通知授权iosForegroundOptions:iOS 前台通知展示策略
2. 登录成功后绑定设备
当前项目由后端将业务 userId 作为极光 alias,App 不再直接调用 jgPush.setAlias()。
原因:
- 后端可以在绑定前统一清理过期和超量设备
userId从登录 Token 获取,客户端不能绑定其他用户- 密码、微信和 Apple 登录共用同一条推送同步路径
- 推送失败属于附属能力失败,不会改变登录认证结果
绑定逻辑已统一放在 src/sheep/stores/user.ts 的 disposeLogin() 中。各登录页面只需在保存 Token 后调用该方法,不要再追加别名操作。
示例:
import { useUserStore } from "@@/stores/user"
const userStore = useUserStore()
const onLoginSuccess = async () => {
// disposeLogin 内部会获取 registrationId 并调用后端绑定接口;推送失败只记录警告。
await userStore.disposeLogin()
}
3. 退出登录时解绑
disposeLogOut() 按以下顺序执行:
- Token 有效时获取当前
registrationId - 调用后端接口精确解绑当前设备
- 调用
jgPush.deleteAlias()作为本地兜底 - 调用
jgPush.cleanTags()清理本地标签 - 调用服务端 Token 注销接口
- 在
finally中清除本地登录态
上述步骤相互隔离,任何一步失败都不能阻止本地退出。强杀 App 或系统回收进程时无法保证执行解绑,后端会通过过期设备策略兜底。
示例:
import { useUserStore } from "@@/stores/user"
async function handleLogout() {
const userStore = useUserStore()
await userStore.disposeLogOut()
}
常用初始化配置
基础初始化
await jgPush.initialize({
debug: true,
autoInitService: true,
})
Android 初始化示例
await jgPush.initialize({
debug: true,
autoInitService: true,
requestAndroidPermission: true,
androidChannel: {
channel: "default",
},
})
Android 自定义渠道和铃声
await jgPush.initialize({
androidChannel: {
channel: "notice",
channel_id: "notice_channel",
sound: "push.caf",
},
})
iOS 初始化示例
await jgPush.initialize({
autoInitService: true,
requestIOSNotificationPermission: true,
iosForegroundOptions: {
alert: true,
sound: true,
badge: true,
},
})
常用状态查询
获取完整状态快照
const status = jgPush.getStatus()
console.log(status)
返回结构示例:
{
platform: "android",
pluginAvailable: true,
initialized: true,
initializing: false,
connected: true,
pushStopped: false,
notificationPermission: "granted",
notificationEnabled: true,
registrationId: "xxxxxx",
alias: "10001",
tags: ["merchant", "rider"],
deviceToken: "",
}
主动刷新状态
await jgPush.refreshStatus()
适用场景:
- 应用从后台回到前台
- 登录后想重新同步
registrationId - 权限变化后想重新读取通知状态
获取注册 ID
const registrationId = await jgPush.getRegistrationId()
console.log(registrationId)
说明:
- 极光刚初始化完成时,
registrationId可能暂时为空 - 这种情况属于正常现象,可以稍后重试
事件监听用法
本模块提供统一的事件订阅接口:
const off = jgPush.on("notification", (event) => {
console.log(event.type)
console.log(event.payload)
console.log(event.raw)
})
off()
支持的事件:
connectnotificationcustomMessageinAppMessagelocalNotificationtagAliasmobileNumberdeviceTokengeofencevoiperror
监听连接状态
const offConnect = jgPush.on("connect", (event) => {
console.log("是否连接成功:", event.payload.connectEnable)
})
监听通知事件
const offNotification = jgPush.on("notification", (event) => {
const payload = event.payload
console.log("通知标题:", payload.title)
console.log("通知内容:", payload.content)
console.log("通知附加字段:", payload.extras)
console.log("通知事件类型:", payload.notificationEventType)
})
说明:
notificationArrived:收到通知notificationOpened:点击通知
如果是点击通知,工具类会尝试从 extras 中提取跳转信息并写入 push-store 的待消费路由队列。
监听自定义消息
const offCustom = jgPush.on("customMessage", (event) => {
console.log("自定义消息内容:", event.payload.content)
console.log("扩展字段:", event.payload.extras)
})
监听错误
const offError = jgPush.on("error", (event) => {
console.error("推送模块错误:", event.payload.code, event.payload.message)
})
清空监听器
jgPush.clearListeners()
只清理某一种事件:
jgPush.clearListeners("notification")
别名与标签管理
底层设置别名
setAlias() 仅作为原生插件底层能力保留,当前项目的登录流程禁止直接调用。业务登录必须提交 registrationId 给后端统一绑定。
await jgPush.setAlias("10001")
本地删除别名
deleteAlias() 只用于退出登录时的本地兜底,不能替代后端精确解绑接口。
await jgPush.deleteAlias()
查询别名
const result = await jgPush.queryAlias()
console.log(result.alias)
新增标签
await jgPush.addTags(["merchant", "canteen"])
覆盖标签
await jgPush.updateTags(["merchant", "vip"])
删除指定标签
await jgPush.deleteTags(["vip"])
清空所有标签
await jgPush.cleanTags()
查询单个标签
const result = await jgPush.queryTag("merchant")
console.log(result.tag, result.tagEnable)
查询全部标签
const result = await jgPush.getAllTags()
console.log(result.tags)
说明:
- 标签/别名接口内部已做 Promise 封装
- 工具类内部会通过
sequence自动关联原生回调 - 回调超时会抛出统一错误
通知相关能力
打开系统通知设置页
await jgPush.openSettingsForNotification()
查询通知权限状态
const result = await jgPush.queryNotificationState()
console.log(result.permission)
console.log(result.enabled)
权限状态说明:
unknowngranteddeniednot_determinedprovisionaldisabled
查询 Android 通知总开关
const enabled = await jgPush.isNotificationEnabled()
查询 iOS 通知授权
const result = await jgPush.requestNotificationAuthorization()
console.log(result.status)
本地通知能力
本模块不仅支持接收服务端推送,也支持主动发本地通知,适合:
- 关键业务提醒
- 前台即时提醒
- 应用内手动触发通知场景
新增本地通知
await jgPush.addLocalNotification({
messageID: "order_10001",
title: "新订单提醒",
content: "您有一笔新的订单,请及时处理",
extras: {
path: "/pages/order/detail",
query: {
id: "10001",
},
},
})
删除指定本地通知
await jgPush.removeLocalNotification("order_10001")
清空本地通知
await jgPush.clearLocalNotifications()
Android 专属能力
停止推送
await jgPush.stopPush()
恢复推送
await jgPush.resumePush()
查询推送是否已停止
const stopped = await jgPush.isPushStopped()
设置通知渠道
await jgPush.setChannel({
channel: "default",
})
设置通知渠道与铃声
await jgPush.setChannelAndSound({
channel: "notice",
channel_id: "notice_channel",
sound: "push.caf",
})
设置允许推送时间
await jgPush.setPushTime({
pushTimeDays: [1, 2, 3, 4, 5],
pushTimeStartHour: 8,
pushTimeEndHour: 22,
})
设置静默时间
await jgPush.setSilenceTime({
silenceTimeStartHour: 22,
silenceTimeStartMinute: 0,
silenceTimeEndHour: 8,
silenceTimeEndMinute: 0,
})
设置最近保留通知数
await jgPush.setLatestNotificationNumber(10)
请求 Android 权限
await jgPush.requestAndroidPermission()
页面统计
await jgPush.onResume()
await jgPush.onPause()
清除通知
await jgPush.clearNotificationById("10001")
await jgPush.clearAllNotifications()
iOS 专属能力
设置前台通知展示策略
await jgPush.setNotiForegroundPresentationOptions({
alert: true,
sound: true,
badge: true,
})
查询系统定位服务
const enabled = await jgPush.locationServicesEnabled()
查询定位授权状态
const status = await jgPush.getLocationAuthorizationStatus()
请求定位授权
await jgPush.requestLocationAuthorization()
初始化 VoIP
await jgPush.initVoipService()
设置位置
await jgPush.setLocation({
latitude: 30.2741,
longitude: 120.1551,
})
设置定位能力开关
await jgPush.setLocationEnable(true)
页面进入与离开
await jgPush.pageEnterTo("订单详情页")
await jgPush.pageLeave("订单详情页")
设置地理围栏最大数量
await jgPush.setMaxGeofenceNumber(10)
删除指定地理围栏
await jgPush.deleteGeofence("beijing")
错误处理方式
本模块统一使用 PushServiceError 作为异常类型。
推荐写法:
import jgPush, { PushServiceError } from "@@/utils/jg-push"
try {
await jgPush.updateTags(["canteen"])
} catch (error) {
if (error instanceof PushServiceError) {
console.error(error.code, error.message, error.detail)
} else {
console.error(error)
}
}
常见错误码:
PUSH_UNSUPPORTED_PLATFORMPUSH_PLUGIN_UNAVAILABLEPUSH_METHOD_UNAVAILABLEPUSH_NOT_INITIALIZEDPUSH_CALLBACK_TIMEOUTPUSH_NATIVE_ERROR
与当前项目结合的接入方案
当前项目已经按以下方式接入:
App 启动时初始化
初始化位于 src/App.vue 的 onLaunch() 内,并使用独立异常捕获,插件或权限初始化失败不会阻断 App 启动。
建议写法:
import jgPush from "@@/utils/jg-push"
onLaunch() {
this._initTabbar()
this._initGlobalWs()
// #ifdef APP-PLUS
jgPush.initialize({
debug: import.meta.env.DEV,
autoInitService: true,
enableCrashHandler: true,
requestAndroidPermission: true,
requestIOSNotificationPermission: true,
iosForegroundOptions: {
alert: true,
sound: true,
badge: true,
},
}).catch((error) => {
console.error("[JPush] init failed", error)
})
// #endif
}
登录成功后同步设备
密码、微信和 Apple 登录成功后都会调用 userStore.disposeLogin()。该方法获取当前 registrationId,调用 /thirdparty/jg-push/device/bind,然后独立更新标签。
登录调用方只保留:
await userStore.disposeLogin()
后端绑定或标签更新失败只记录警告,不向登录页面抛出,也不触发统一错误 Toast。
退出登录时精确解绑并清理本地状态
退出逻辑统一位于 src/sheep/stores/user.ts 的 disposeLogOut()。
页面只需调用:
await userStore.disposeLogOut()
App 原生推送逻辑均位于 APP-PLUS 条件编译分支,H5 不调用极光原生插件。
待跳转路由说明
如果通知点击事件的 extras 中带有以下字段:
pathqueryroutePathrouteQueryparamsurl
工具类会尝试自动提取并写入 push-store.pendingRoutes。
业务层可以这样消费:
import { usePushStore } from "@@/stores/push-store"
import { router } from "@@/index"
const pushStore = usePushStore()
const nextRoute = pushStore.shiftPendingRoute()
if (nextRoute) {
router.jump(nextRoute.path, nextRoute.query || {})
}
适合场景:
- 冷启动后延迟跳转
- 首页 ready 后统一处理通知跳转
- 避免通知点击过早发生时页面尚未挂载
常见问题
1. H5 为什么不能用?
因为极光 HBuilderX 插件是原生插件,依赖:
uni.requireNativePlugin("JG-JPush")
这不是 H5 能直接使用的能力,所以 H5 会自动走不支持分支。
2. 为什么 registrationId 有时为空?
这是正常现象,常见原因:
- SDK 刚初始化
- 网络尚未连上
- 极光注册流程尚未完成
建议做法:
- 初始化后稍后重试
- 在
onShow或登录后再调用一次refreshStatus()
3. 为什么调用标签或别名接口会超时?
常见原因:
- 原生插件未正确初始化
- 当前平台不支持该方法
- 原生层没有正确回调
- 网络或设备状态异常
模块内部会抛出:
PUSH_CALLBACK_TIMEOUT
4. 推送状态在哪里看?
可以直接看 push-store:
const pushStore = usePushStore()
console.log(pushStore)
也可以调用:
console.log(jgPush.getStatus())
业务接入顺序
新增登录方式或推送业务时按这个顺序接入:
- 在
App.vue初始化推送,并独立捕获初始化异常 - 登录成功后统一调用
userStore.disposeLogin(),由后端绑定设备 - 退出登录统一调用
userStore.disposeLogOut(),由后端精确解绑当前设备 - 在首页或业务层加通知事件监听
- 根据业务需要再补
tags、本地通知、跳转队列处理
总结
当前 jg-push 模块已经具备以下能力:
- 极光原生插件初始化
- Android / iOS 差异化处理
- 推送状态全局维护
- 通知、自定义消息、应用内消息统一监听
- 别名、标签、角标、本地通知、权限、地理能力完整封装
- 错误统一包装
- 与当前项目的登录态和全局状态体系兼容
- 后端统一管理 alias 容量、过期设备和精确解绑
- 登录认证与推送同步相互隔离