一个面向 Expo prebuild / Dev Client 的 JPush Config Plugin。
自动注入 iOS / Android 原生配置,完整支持主流 Android 厂商通道,保障推送送达率。
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.xml、build.gradle、settings.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 |
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推荐使用 app.config.ts,并把 Android 的敏感值交给环境变量或 gradle.properties。
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 --cleanimport 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,
},
},
},
],
],
};
};appKey、channel、packageName仍然是插件必填项- 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 读取优先级如下:
System.getenv("...")project.findProperty("...")- 插件收到的配置值:
JPUSH_PKGNAME、JPUSH_APPKEY、JPUSH_CHANNEL - 空字符串,其余未提供默认值的字段
| 变量名 | 说明 |
|---|---|
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 厂商通道参数获取
appKey:JPush 后台创建应用后获得的 AppKeychannel:渠道标识,默认developer-defaultapsForProduction:是否使用生产环境 APNs,默认false(开发环境)
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 |
注入依赖、manifestPlaceholders、abiFilters、厂商插件 |
| Android | android/build.gradle |
注入厂商 Maven 仓库与 classpath |
| Android | android/settings.gradle |
注册 jpush-react-native / jcore-react-native 模块 |
| Android | android/gradle.properties |
写入华为 AGC 兼容性开关 |
重新执行 expo prebuild 后,建议检查:
Info.plist中存在:JPUSH_APPKEYJPUSH_CHANNELJPUSH_APS_FOR_PRODUCTION
UIBackgroundModes会保留宿主已有值,并补齐:fetchremote-notification
AppDelegate.swift中存在JPUSHService.setupAppDelegate.swift中的调试日志与JPUSHService.setDebugMode()会被#if DEBUG包裹,release 构建不再无条件打印- 如果项目使用 Swift,插件会优先复用已有
SWIFT_OBJC_BRIDGING_HEADER;未配置时会自动创建<target>-Bridging-Header.h
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在构建时覆盖这些值 - 厂商通道密钥如果需要按环境切换,也应沿用同样的覆盖策略
不支持。JPush 需要原生工程和原生依赖,必须通过 expo prebuild 进入 Dev Client 或原生构建流程。
因为参数校验和 Info.plist 注入都发生在 Expo 配置阶段。它们不会再被直接拼进 AppDelegate.swift,但仍然需要在配置阶段提供。
如果你遇到类似 com.android.tools.build:gradle is no set in the build.gradle file 的错误,需要检查业务项目自己的 android/build.gradle 与 Expo 版本是否匹配。这不是本插件主动引入的行为变更。
不建议。重装依赖后会丢失,正式方式建议使用 pnpm patch mx-jpush-expo 或维护自己的 fork。
npm 包入口是根目录的 app.plugin.js,它会加载发布产物 plugin/build。这意味着:
- 仓库开发者在发布新版本前,需要先执行
npm run build package.json的files已包含app.plugin.js与plugin/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。