Skip to content

konodioda727/JPush-Expo

Repository files navigation

MX-JPush-Expo

一个面向 Expo prebuild / Dev Client 的 JPush Config Plugin。
自动注入 iOS / Android 原生配置,完整支持主流 Android 厂商通道,保障推送送达率。

npm version CI status license Expo SDK 53+ Node.js >=18.18.0

Important

JPush 不支持 Expo Go。本项目面向 expo prebuild 后的原生工程,适用于 Dev Client 或原生构建流程。

目录

为什么使用它

mx-jpush-expo 把 Expo 项目接入 JPush 时最容易反复手改的原生步骤,收敛成一次 expo prebuild

  • 自动写入 iOS Info.plist 的 JPush 配置和后台模式
  • 自动注入 iOS AppDelegate.swift 的 JPush 初始化与回调代码
  • 自动复用或创建 Swift Bridging Header
  • 自动修改 Android AndroidManifest.xmlbuild.gradlesettings.gradle
  • 支持华为、FCM、小米、OPPO、VIVO、魅族、荣耀、蔚来等厂商通道注入
  • 敏感参数支持环境变量注入,避免密钥明文提交到代码仓库

适合这些场景:

  • 你使用 Expo,但需要 JPush 和厂商通道能力
  • 你希望把原生改动交给 Config Plugin 管理,而不是手改生成代码
  • 你需要在 CI / 团队协作里稳定复现 prebuild 输出
  • 你需要严格的密钥安全管理,避免敏感信息泄露

支持矩阵

项目 版本
Expo SDK 53+
仓库开发基线 Expo SDK 53
React Native 0.76.9
Node.js >= 18.18.0
jpush-react-native 3.1.9
jcore-react-native 2.3.0

快速开始

1. 安装依赖

npm install mx-jpush-expo
npm install jpush-react-native@3.1.9 jcore-react-native@^2.3.0

或使用 pnpm

pnpm add mx-jpush-expo
pnpm add jpush-react-native@3.1.9 jcore-react-native@^2.3.0

2. 配置插件

推荐使用 app.config.ts,并把 Android 的敏感值交给环境变量或 gradle.properties

3. 生成原生工程

npx expo prebuild

只刷新 Android:

npx expo prebuild -p android

如果你修改了 app.config.ts / app.json 中的插件参数,需要重新执行一次 prebuild,让最新配置重新落到原生工程。已经生成过原生目录时,建议按平台增量刷新;如果怀疑宿主工程里有历史残留,再使用 --clean 重新生成。

npx expo prebuild -p android --clean
npx expo prebuild -p ios --clean

推荐配置

import type { ConfigContext, ExpoConfig } from 'expo/config';
import 'dotenv/config';

export default ({ config }: ConfigContext): ExpoConfig => {
  const isProduction = process.env.EXPO_PUBLIC_ENV === 'production';

  return {
    ...config,
    plugins: [
      ...(config.plugins || []),
      [
        'mx-jpush-expo',
        {
          appKey: process.env.JPUSH_APP_KEY ?? '',
          channel: process.env.JPUSH_CHANNEL ?? 'developer-default',
          packageName:
            process.env.JPUSH_PKGNAME ?? config.android?.package ?? '',
          apsForProduction: isProduction,
          vendorChannels: {
            huawei: { enabled: true },
            fcm: { enabled: true },
            xiaomi: {
              appId: process.env.JPUSH_XIAOMI_APP_ID,
              appKey: process.env.JPUSH_XIAOMI_APP_KEY,
            },
            oppo: {
              appId: process.env.JPUSH_OPPO_APP_ID,
              appKey: process.env.JPUSH_OPPO_APP_KEY,
              appSecret: process.env.JPUSH_OPPO_APP_SECRET,
            },
            vivo: {
              appId: process.env.JPUSH_VIVO_APP_ID,
              appKey: process.env.JPUSH_VIVO_APP_KEY,
            },
            meizu: {
              appId: process.env.JPUSH_MEIZU_APP_ID,
              appKey: process.env.JPUSH_MEIZU_APP_KEY,
            },
            honor: {
              appId: process.env.JPUSH_HONOR_APP_ID,
            },
            nio: {
              appId: process.env.JPUSH_NIO_APP_ID,
            },
          },
        },
      ],
    ],
  };
};

配置要点

  • appKeychannelpackageName 仍然是插件必填项
  • iOS 初始化参数会写入 Info.plist,不再直接拼进 AppDelegate.swift
  • Android manifestPlaceholders 优先读取环境变量或 gradle.properties,缺失时会回退到插件配置里的 appKey / channel / packageName
  • 如果宿主已经定义了 manifestPlaceholders,插件会通过 manifestPlaceholders += [...] 追加 JPush 字段 非 JPush 的宿主键会被保留;如果宿主和插件都声明了同名的 JPUSH_* 键,后追加的 JPush 默认值会生效
  • Android app/build.gradle 注入不再依赖 versionName 所在行;缺少 versionName 或使用 Gradle 变量时也能稳定 prebuild
  • vendorChannels 决定要注入哪些厂商 SDK 与占位符;若声明某个厂商通道,就必须提供该厂商要求的必填字段
  • 厂商密钥仍然建议交给环境变量,避免把敏感信息直接提交到仓库

环境变量与厂商通道

Android 端的 manifestPlaceholders 读取优先级如下:

  1. System.getenv("...")
  2. project.findProperty("...")
  3. 插件收到的配置值:JPUSH_PKGNAMEJPUSH_APPKEYJPUSH_CHANNEL
  4. 空字符串,其余未提供默认值的字段

可用环境变量

变量名 说明
JPUSH_APP_KEY JPush AppKey
JPUSH_CHANNEL JPush Channel
JPUSH_PKGNAME Android 包名
JPUSH_XIAOMI_APP_ID / JPUSH_XIAOMI_APP_KEY 小米通道
JPUSH_OPPO_APP_ID / JPUSH_OPPO_APP_KEY / JPUSH_OPPO_APP_SECRET OPPO 通道
JPUSH_VIVO_APP_ID / JPUSH_VIVO_APP_KEY VIVO 通道
JPUSH_MEIZU_APP_ID / JPUSH_MEIZU_APP_KEY 魅族通道
JPUSH_HONOR_APP_ID 荣耀通道
JPUSH_NIO_APP_ID 蔚来通道

示例 .env

JPUSH_APP_KEY=your-jpush-app-key
JPUSH_CHANNEL=developer-default
JPUSH_PKGNAME=com.your.app
JPUSH_XIAOMI_APP_ID=your-xiaomi-app-id
JPUSH_XIAOMI_APP_KEY=your-xiaomi-app-key

厂商通道额外要求

厂商 额外文件 签名要求 说明
华为 agconnect-services.json 需要 需配置 SHA256 指纹
FCM google-services.json 不需要 需在 Firebase 控制台申请
荣耀 需要 需配置 SHA256 指纹
蔚来 需要 需配置应用签名
小米 不需要 仅需 AppId / AppKey
OPPO 不需要 仅需 AppId / AppKey / AppSecret
VIVO 不需要 仅需 AppId / AppKey
魅族 不需要 仅需 AppId / AppKey

官方参数说明见:极光推送 Android 厂商通道参数获取

配置说明

iOS 配置

  • appKey:JPush 后台创建应用后获得的 AppKey
  • channel:渠道标识,默认 developer-default
  • apsForProduction:是否使用生产环境 APNs,默认 false(开发环境)

Android 配置

  • packageName:Android 应用包名,用于 manifestPlaceholders
  • 厂商通道通过 vendorChannels 对象配置,每个厂商独立开关

插件会修改哪些原生文件

平台 文件 作用
iOS ios/<app>/Info.plist 写入 JPUSH_* 配置并合并 UIBackgroundModes
iOS ios/<app>/AppDelegate.swift 注入 JPush 初始化、APNs 回调和代理扩展
iOS ios/<app>/<target>-Bridging-Header.h 复用或创建桥接头文件,保证 import 幂等
Android android/app/src/main/AndroidManifest.xml 写入 JPUSH_APPKEY / JPUSH_CHANNEL meta-data
Android android/app/build.gradle 注入依赖、manifestPlaceholdersabiFilters、厂商插件
Android android/build.gradle 注入厂商 Maven 仓库与 classpath
Android android/settings.gradle 注册 jpush-react-native / jcore-react-native 模块
Android android/gradle.properties 写入华为 AGC 兼容性开关

如何验证生成结果

重新执行 expo prebuild 后,建议检查:

iOS

  • Info.plist 中存在:
    • JPUSH_APPKEY
    • JPUSH_CHANNEL
    • JPUSH_APS_FOR_PRODUCTION
  • UIBackgroundModes 会保留宿主已有值,并补齐:
    • fetch
    • remote-notification
  • AppDelegate.swift 中存在 JPUSHService.setup
  • AppDelegate.swift 中的调试日志与 JPUSHService.setDebugMode() 会被 #if DEBUG 包裹,release 构建不再无条件打印
  • 如果项目使用 Swift,插件会优先复用已有 SWIFT_OBJC_BRIDGING_HEADER;未配置时会自动创建 <target>-Bridging-Header.h

Android

android/app/build.gradle 中的 JPush 占位符应保持"运行时读取",而不是写死明文:

manifestPlaceholders += [
    JPUSH_PKGNAME: System.getenv("JPUSH_PKGNAME") ?: (project.findProperty("JPUSH_PKGNAME") ?: "com.your.app"),
    JPUSH_APPKEY: System.getenv("JPUSH_APP_KEY") ?: (project.findProperty("JPUSH_APP_KEY") ?: "your-jpush-app-key"),
    JPUSH_CHANNEL: System.getenv("JPUSH_CHANNEL") ?: (project.findProperty("JPUSH_CHANNEL") ?: "developer-default")
]

也就是说:

  • 宿主原本的 manifestPlaceholders = [...] 配置会被保留
  • 对于宿主和插件都声明的 JPUSH_* 键,后追加的 JPush 值会在合并结果中生效
  • 对最终消费方,插件配置本身已经足够让 prebuild 产出可用的 Android 默认值
  • 对 CI / EAS / 多环境发布,依然推荐通过环境变量或 gradle.properties 在构建时覆盖这些值
  • 厂商通道密钥如果需要按环境切换,也应沿用同样的覆盖策略

常见问题

是否支持 Expo Go?

不支持。JPush 需要原生工程和原生依赖,必须通过 expo prebuild 进入 Dev Client 或原生构建流程。

为什么 iOS 仍然要求在插件配置里填写 appKey / channel

因为参数校验和 Info.plist 注入都发生在 Expo 配置阶段。它们不会再被直接拼进 AppDelegate.swift,但仍然需要在配置阶段提供。

Android 遇到 Gradle 插件版本错误怎么办?

如果你遇到类似 com.android.tools.build:gradle is no set in the build.gradle file 的错误,需要检查业务项目自己的 android/build.gradle 与 Expo 版本是否匹配。这不是本插件主动引入的行为变更。

直接改 node_modules/mx-jpush-expo 可以吗?

不建议。重装依赖后会丢失,正式方式建议使用 pnpm patch mx-jpush-expo 或维护自己的 fork。

插件发布后,消费方实际会拿到什么?

npm 包入口是根目录的 app.plugin.js,它会加载发布产物 plugin/build。这意味着:

  • 仓库开发者在发布新版本前,需要先执行 npm run build
  • package.jsonfiles 已包含 app.plugin.jsplugin/build,消费方不会直接运行 plugin/src 中的 TypeScript 源码
  • 如果你维护的是 fork 或 patch 包,发布前请先确认编译产物已同步更新,否则消费项目仍会执行旧逻辑

开发与测试

npm run build
npm run test -- --runInBand
npm run lint

测试覆盖重点

  • iOS Info.plist 合并与 Bridging Header 创建 / 幂等
  • iOS AppDelegate.swift 注入与幂等
  • Android Manifest、Gradle、Settings 和 gradle.properties 原生输出
  • fixture-based 回归测试,确保 compileModsAsync 输出稳定

更多开发细节见 DEVELOPMENT.md

项目结构

mx-jpush-expo/
├── app.plugin.js
├── plugin/
│   ├── src/
│   │   ├── index.ts
│   │   ├── types.ts
│   │   ├── ios/
│   │   │   ├── infoPlist.ts
│   │   │   ├── appDelegate.ts
│   │   │   └── bridgingHeader.ts
│   │   ├── android/
│   │   │   ├── androidManifest.ts
│   │   │   ├── appBuildGradle.ts
│   │   │   ├── projectBuildGradle.ts
│   │   │   ├── settingsGradle.ts
│   │   │   └── gradleProperties.ts
│   │   └── utils/
│   │       ├── codeValidator.ts
│   │       ├── generateCode.ts
│   │       └── sourceCode.ts
│   ├── __tests__/
│   │   ├── fixtures/
│   │   ├── iosFixture.ts
│   │   ├── androidFixture.ts
│   │   └── *.test.ts
│   └── build/
├── .github/workflows/ci.yml
├── CHANGELOG.md
├── DEVELOPMENT.md
└── README.md

插件内部说明见 plugin/README.md

最近更新

完整更新历史请查看 CHANGELOG.md

  • iOS UIBackgroundModes 改为合并写入,不再覆盖宿主已有后台模式
  • Swift Bridging Header 支持优先复用、缺失自动创建,并保持幂等
  • 补齐 iOS / Android fixture-based 原生回归测试
  • Android manifestPlaceholders 改为在宿主现有配置后追加,不再依赖 versionName 文本锚点
  • iOS AppDelegate.swift 中的 JPush 调试日志只在 DEBUG 构建启用
  • 加入 ESLint 与 CI 质量闭环
  • 对齐 Expo SDK 53 的版本声明与仓库开发基线
  • Android 敏感参数支持环境变量 / gradle.properties 读取,不再明文写入构建脚本
  • iOS 初始化参数改为从 Info.plist 读取,不再直接注入 AppDelegate.swift

致谢与许可

感谢以下资料与实现思路的启发:

本项目使用 MIT License

Packages

 
 
 

Contributors