
1. 先别急着改代码这个问题的真实链路到底是哪断的做uniapp推送的老鸟们应该都有过这种经历开发环境下拿真机调试JPush的账号登录、通知弹出都正常得不得了结果打包上架之后用户把App切到后台、锁屏放一会儿或者干脆从最近任务里划掉就再也没有任何通知进来了。更诡异的是用户解锁手机、重新打开App的一瞬间通知唰地一下全冒出来了——这说明推送没有丢只是手机在应用进程被杀的情况下收不到。我当初接到这个华为离线推送问题时第一反应也是去查JPush的初始化代码、AppKey配置、权限声明结果代码翻了三遍控制台测试推送也是送达成功可华为手机就是死活不弹通知。后来才反应过来开发环境之所以正常是因为应用进程还活着JPush自己的长连接还能用一旦进程被系统回收唯一还能把通知从服务器送到用户手机的通道是华为的厂商推送服务而不是JPush。这里先把这个核心逻辑捋清楚后面所有排查才有方向JPush本身维护了一条应用内的长连接通道只要App进程存活推送就能实时到达。应用被用户杀掉或系统回收后这条长连接就断了。此时安卓系统尤其是华为EMUI/HarmonyOS只认系统级推送服务也就是华为推送服务HMS Push它由系统组件代收消息后转成通知栏展示。所以uniappJPush要支持华为离线推送实际链路是服务端调JPush API → JPush把消息转给华为PUSH → 华为系统组件收下并发通知栏通知。任何一环没打通离线就收不到。也就是说这个问题的本质不是uniapp代码写错了而是厂商通道根本没有接通。接下来按我踩坑的顺序把从注册到配置再到底层原因的每个环节都过一遍。2. 华为离线推送的前置条件没开通厂商通道的JPush和普通本地通知没区别2.1 华为开发者平台那边的三件套AppID、AppSecret、SHA256指纹厂商通道的接入第一步根本不在uniapp工程里而在华为开发者联盟AppGallery Connect后台。很多人漏掉的不是AppID和AppSecret而是SHA256证书指纹。具体操作是登录华为开发者联盟创建一个应用包名必须和你的uniapp打包包名完全一致这是最容易错的然后在开发服务-推送服务里开通推送功能接着记下两个东西AppID和AppSecret。同时在应用信息里配置SHA256证书指纹——这个指纹来自你的签名证书不是调试证书是用在正式发布包上的那个keystore。我当时第一次配的时候用了debug.keystore的指纹结果JPush控制台那边能配上但厂商通道鉴权直接失败消息在华为后台根本投不出去。后来换成正式签名后重新生成指纹再更新到华为平台才正常。指纹生成命令没什么神秘的一行就够了keytool -list -v -keystore your-release-key.jks -alias your-alias -storepass your-password把输出的SHA256证书指纹整个复制到华为后台就行注意不要把冒号删掉或者改成小写格式必须原样。2.2 JPush控制台里填的不是AppKey而是厂商推送配置这个坑我身边好几个同事都踩过。在极光推送Web控制台里有一个推送设置→厂商推送配置的入口里面分成小米、华为、荣耀、OPPO、vivo、魅族、FCM等各个厂商通道的配置项。华为那一栏需要填的就是在华为开发者后台拿到的AppID和AppSecret。有个细节值得单独拎出来说JPush这个入口的包名字段必须和你在华为平台填入的包名一致连大小写都不能差。一旦不一致JPush这边虽然能保存但真实运行时消息根本不会报错给你只在JPush控制台的消息详情里显示厂商通道发送失败或未匹配到设备。如果你到现在还没看过JPush控制台的消息详情列表建议马上打开看看绝大部分离线收不到的案子都能在那里找到真正的原因。2.3 uniapp端的manifest配置和云打包参数你到底有没有把JPush插件和HMS Core打进去uniapp项目里JPush的使用方式分两种一种是通过uni_modules市场里的uni-jpush插件另一种是HBuilderX的原生App-JPush模块勾选之后云打包。区别很大但也各有各的坑。如果是用uni_jpush这种插件方式它的底层其实是集成了JPush原生SDK而JPush原生SDK要支持华为离线推送必须依赖华为的HMS Core SDK。你在manifest.json的App模块配置里找到推送相关的模块确认JPush已勾选并且还要看看有没有Push在线推送/离线推送这类描述。实际打包时JPush官方SDK会把HMS Core的库一起打进去但HBuilderX的不同版本对HMS Core的集成方式有差异建议云打包时选使用云端证书然后把manifest里appid、appkey、华为的appid/secret都填全。我知道很多人为了方便走的是离线打包用的是Android Studio自己改工程。这种情况下你还需要在Android原生工程里手动添加HMS Core的依赖并在build.gradle里加上华为的maven仓库。这一步如果漏了哪怕uniapp端配置全对华为离线通道也起不来。这里直接放一份我实测可用的manifest.json推送相关片段云打包场景供参考Push: { version: 1.0.0, provider: jpush, appkey: 你的极光AppKey, huawei: { appid: 华为AppID, appsecret: 华为AppSecret } }注意不同的HBuilderX版本、不同年代的manifest里字段嵌套结构会有差异最稳妥的做法是在HBuilderX的manifest可视化界面里直接找到推送模块把界面上的每一项都填了不要手改json文件。可视界面会动态检查必填项手改json一旦漏字段编译期往往不报错运行期才静默失败。3. 真正导致华为离线收不到推送的几个隐形杀手3.1 服务端推送时没指定厂商通道消息白白走了JPush长连接很多团队只在前端集成了JPush SDK服务端只是简单调一下JPush的REST API传入标题、内容和registration_id然后就以为万事大吉。这里就是最隐蔽也是最普遍的坑JPush的API在推送时如果不定制厂商通道参数默认会走极光的自建通道下发。自建通道的规则是设备在线就即时到达设备离线时消息虽会暂存JPush默认保存离线消息但暂存时间一到就会丢弃而且手机端app进程被杀死后自建通道根本无法唤醒系统弹通知。所以服务端在推给华为设备时必须在推送请求体里明确指定厂商通道。参考JPush Push API的结构你要在notification里针对android节点加channel_id或直接设置extras里的n_channel等字段让JPush识别出这条消息需要走华为厂商通道。常见写法是{ platform: [android], audience: { registration_id: [xxxxxxxx] }, notification: { android: { alert: 离线也能收到的通知, title: 测试标题, extras: { n_channel: huawei_push } } } }这里的n_channel在不同版本JPush API里可能以不同key出现建议以官方最新文档为准。但核心思路是不变的给华为设备推送必须把消息标记为走厂商通道否则离线就收不到。我和团队后端排查这个问题时最典型的表现就是JPush控制台里消息状态显示已送达但华为手机没反应——因为送达的是JPush通道而JPush通道此时根本没有渠道把通知渲染出来。3.2 没有配置通知渠道Notification Channel或渠道级别不对Android 8.0API 26之后通知必须绑定到具体的Notification Channel。JPush在华为离线推送时收到的厂商通道消息最终会通过华为系统组件创建通知但这个通知也要落到一个channel上。如果JPush SDK初始化时没有默认创建channel或者你在小米、华为上给了错误的channel配置那么通知可能被系统静默拦截。尤其是一些国产ROM还有自己的通知管理策略——对不重要的channel默认不展示。JPush SDK本身提供了一个默认channel但很多uniapp项目接入JPush插件时并没有去设置channel。我后来在项目里强制指定了一个高重要性的channel问题才批量解决const jpushModule uni.requireNativePlugin(JPush); if (jpushModule) { jpushModule.setPushTime({ pushTime: { startHour: 0, endHour: 23 } }); }实际上uniapp端能暴露出来的JPush自定义能力很有限如果你发现SDK版本太老、连channel都建不了优先考虑升级插件版本或改用离线打包后在原生层初始化JPush再配合自己的channel配置。这不是小事华为ROM对低重要性channel的通知默认就是静默放入通知中心但不响铃很多用户感知就是没收到通知。3.3 华为手机端的自启动管理和应用启动管理拦截了HMS Push的接收即使上面所有配置都对了还有最后一层完全在手机侧的拦截华为的手机管家为了省电默认会限制应用自启动同时也有可能限制HMS Push的后台运行。不过这里有个细节多数人没意识到——华为离线推送的接收方其实不是你的App而是HMS Core华为移动服务的系统组件。系统组件本身不太容易被限制但阉割版、旧版HMS Core、或者用户在设置里手动停用了华为移动服务都会导致厂商通道失效。我在一台旧华为P30上测试时发现问题竟然出在HMS Core被用户停用上。恢复启用后离线推送立刻通了。所以遇到线上反馈离线收不到时先别怀疑代码先让用户检查设置→应用→应用管理→华为移动服务→是否已停用。这虽然是用户侧问题但作为开发者必须知道这个链路否则你改一天代码都解决不了。3.4 华为AppGallery Connect的推送服务没上线或者应用没通过审核另一个不容易察觉但影响巨大的事你在华为开发者平台创建应用后推送服务虽然可以开通但如果应用还没上架华为应用市场或者推送服务模块没走完审核流程华为推送服务也可能处于受限状态。有人问我在测试阶段就收不到厂商推送测试环境下华为后台对未上架应用的推送限制比较宽松常见还是要等权限就绪。不过实测下来新注册的开发者账号、新建的应用至少需要完成身份认证然后推送服务才会真正可用否则离线推送一律失败。这一步如果挂了你在服务端、客户端折腾再多也没用。排查方法是登录华为开发者联盟后台找到你的应用点进推送服务看状态是否正常可用。如果看到服务未开通或能力受限先按平台提示补齐资料。4. 一套可以抄作业的完整排障流程从JPush控制台反推到底卡在哪一环4.1 第一步先在JPush控制台发一条厂商通道测试消息看状态字段JPush控制台的消息列表每一条消息都有送达详情。点进去之后关键看这几个字段推送目标里的厂商通道状态JPush会把一条消息拆成多个子任务每个子任务对应一个通道。如果你看到JPush通道送达华为通道没送达或者已忽略那问题就出在厂商通道对接那一环。失败原因常见的有厂商AppID/AppSecret错误设备未注册厂商token包名不一致。其中包名不一致尤其常见因为uniapp的Android包名在打包时用的是Android包名字段很多人填了测试包名或在不同平台填了不同包名结果华为平台、JPush平台、应用市场三处各不一样。这一条测试消息基本能帮你把问题范围缩小到80%。我的经验法则是厂商通道状态显示成功手机还收不到基本就是手机系统设置或HMS Core状态问题厂商通道状态失败就是配置或token问题。4.2 第二步用华为后台的推送调试直接发一条透传/通知消息这一步是为了排除JPush那部分的问题。在华为AppGallery Connect的推送服务后台有个调试功能可以直接给某台设备发一条通知。如果华为后台直推都能收到说明你的设备、HMS Core、应用注册通道是好的如果华为后台也推不到那问题可能出在手机系统、HMS Core版本或者华为平台的应用配置上。当时我做完这一步就分清了华为后台直推没问题JPush消息详情显示华为通道发送成功但手机就是没通知。这说明消息已经到了华为系统组件但没有被系统正确地转成用户可见的通知——最终把注意力引向了Notification Channel和通知权限。4.3 第三步检查Android 13的通知权限运行时申请这里需要特别提醒目标版本是Android 13targetSdk 33以上的应用通知权限变成了运行时权限必须在代码中动态申请POST_NOTIFICATIONS。uniapp如果是在什么都没有配置的情况下云打包默认targetSdk可能已经是30以上但很多项目升级targetSdk到33后用户安装App后没有主动打开通知权限在线时可能还因为App在前台而能看到通知但一旦App被切到后台、进程死了华为厂商通道的接管也会因为没授权而失败。对厂商通道也一样依赖通知权限因为最终要呈现通知栏消息。在uniapp里可以加一段原生插件写权限申请或者用plus.android申请权限也可以用uni的Api简单示例// #ifdef APP-PLUS const main plus.android.runtimeMainActivity(); plus.android.requestPermissions( main, [android.permission.POST_NOTIFICATIONS], function(result) { console.log(通知权限申请结果: JSON.stringify(result)); }, function(error) { console.log(通知权限申请失败: JSON.stringify(error)); } ); // #endif申请时机要放在App启动后尽早调用但如果用户拒绝需要引导到系统设置页手动打开。不做这一步Android 13真机就是会悄悄砍掉通知且没有任何报错排查难度极高。4.4 第四步确认华为设备是否真的离线以及是否注册了华为token最后一步的排查对象是JPush SDK有没有成功拿到华为的push token并上报给JPush服务端。在uniapp里可以通过JPush模块的获取registrationId回调来确认再在JPush控制台里看这个设备的registration_id关联的厂商token状态。如果华为token为空说明HMS Core初始化时没有成功生成push token或者JPush SDK的厂商初始化被系统拦截了。实践中最常见的原因是App启动时网络还没完全就绪HMS Core的token初始化失败后没有自动重试。JPush SDK内部通常有重试机制但如果你发现用户设备长期没有华为token可以考虑在每次App回到前台时主动调用一次初始化接口来触发补拉。虽然这不是官方推荐做法但在我处理的一个线上案例里通过App前后台切换时重试初始化有效提高了token注册成功率。5. 离线推送的进阶操作服务端、客户端和运营侧要三线配合5.1 服务端要注意的离线消息保存时长和厂商通道优先级JPush自建通道的离线消息是有时效的默认可以保存一段时间但厂商通道的离线消息策略不一样华为推送通道有自己的一套缓存逻辑并且对厂商通道下发和系统通知栏消息的处理也不同。所以服务端设计上对重要通知比如支付结果、订单状态建议把离线保存时间设长一些并把厂商通道标记为强制而对普通营销通知则不需要关心离线到达率。JPush API里有一个option.time_to_live参数可以控制离线消息存活时间。这是我踩过的一个坑我们当时默认设置了这个值很小用户锁屏十几分钟后消息就过期了而华为厂商通道是即时下发并不会长时间缓存消息最终导致看似离线收不到实际是消息已经过期了。建议交易类通知time_to_live设为86400秒一天营销类通知不设或设为短时间同时设置override_msg_id做去重防止线上线下重复推送5.2 客户端要处理的通知点击和数据事件别只盯着弹出通知厂商通道和JPush在线通道通知的点击行为有很多差异。在华为离线推送下发时如果点击通知需要跳转页面就不能指望JPush传统的click事件能100%触发——因为App进程是被系统重新拉起来的uniapp里App的onLaunch生命周期会先执行此时你需要把华为推送带过来的extras参数缓存起来等路由就绪后再做跳转。我项目里遇到的情况是华为离线通知能收到点击后App启动起来了但页面参数丢了用户看到的是首页不是他本来想看的订单详情页。后来在plus.runtime.arguments里拿华为的intentUri来解析参数才解决。整理下来做法是在App.vue的onLaunch里解析plus.runtime.arguments找到华为推送的意图数据。用标志变量暂存参数等首页onLoad完成后再导航到目标页。这个思路对JPush在线通道、厂商离线通道都适用保证点击通知时无论进程存活还是被杀都能正确跳转。5.3 运营发推送时也容易踩的坑标题/内容太短或被华为智能屏蔽华为对通知的使用体验有一套自己的判断机制如果通知内容太少、没有标题或者标题和内容重复度极高系统AI可能把通知折叠或延迟展示。我遇到过用户反馈只看得到一个小圆点没有横幅通知最后发现是内容只写了几个字触发了华为的智能通知管理。不是所有用户都能接受你让他去设置里关掉智能通知管理所以更稳妥的处理是每条通知的标题不少于6个汉字内容不少于15个汉字避免标题和正文完全一样尽量带上跳转参数让用户感知到这是一条有内容的通知当然厂商通道的样式限制你也得知道华为离线通知不展示大图只有Icon、标题、内容三行。很多运营在设计推送素材时没注意测试时看到在线通知有大图离线通知却只有一个缩略图于是误报推送坏了。这不是故障是厂商通道的正常限制。6. 多厂商离线推送的统一策略华为通了小米、OPPO、vivo还会远吗处理完华为之后你会发现同样的逻辑适用于其他厂商小米走小米推送OPPO走OPPO Pushvivo走vivo Push。它们在JPush控制台里都要单独配置各自的AppID和AppSecret在设备端也分别依赖各自的系统推送服务。如果你们团队是在做面向C端用户的App我强烈建议从一开始就按多厂商通道的思维来设计JPush这边能申请的都要申请不要只配华为。每多接一个厂商通道离线到达率就往上走一截。但相应的每个厂商的审核、AppID申请、密钥配置都是独立流程提前规划时间别等上线前两天再临时抱佛脚。一个小技巧JPush控制台提供一个厂商通道配置概览页面能看到当前所有厂商通道的配置状态和最近几条消息的送达情况。我建议把它加进每次发版前的自检清单里发完版本顺手截个图比到时候出了问题再回头排查要省事得多。7. 几个我实测中真正起作用的取巧办法最后分享几个不属于官方推荐配置但在我实际排障中有效的手段。按需求自取给HMS Core更新到最新版华为推送服务和系统组件版本高度相关如果手机上的HMS Core版本太老离线推送会存在各种兼容bug。后台没法强制用户更新但可以在引导页或设置页提示用户打开华为应用市场更新HMS Core。JPush SDK版本别太旧极光在不同时期对华为推送的适配有大量更新尤其在HMS Push 5.x、6.x升级时旧版JPush厂商插件可能完全失效。遇到以前能收到最近收不到的反馈先查JPush SDK版本再查HMS Core版本。HBuilderX的云端打包和离线打包选型如果你在HBuilderX云打包后推送完全没反应可以尝试改用离线打包在Android工程里手动集成JPush官方SDK。云打包受HBuilderX内置SDK版本限制离线打包则给你100%的自主权。对我而言重要项目我基本都是离线打包可控性完全不一样。在华为后台的消息回执里看真实失败原因华为推送服务后台的消息列表会记录每条消息的回执状态包括设备离线token失效APP进程被杀死后未解锁等信息。很多在JPush控制台看不到的细节在这里可以看到。这条特别有用。说到底uniappJPush的华为离线推送问题根本不在uniapp怎么写这个层面而是在从服务端到厂商后台再到手机系统的整条链路上。如果你还在纠结代码逻辑不如先把JPush控制台的消息详情翻出来看一遍定位到具体环节后再动手改。我个人的习惯是把这条链路的关键状态位全部做成可观测的指标每次版本迭代后都过一遍能省下大把半夜被人叫起来的痛苦时间。