CodePush停服后,如何用腾讯Shiply快速实现ReactNative热更新?
1. 背景
CodePush是微软VisualStudioAppCenter服务的一个子功能,支持动态下发RN热更新产物。
但是VisualStudioAppCenter 2025/3/31 已经停止服务,如果想继续使用CodePush需要自己独立部署。
专门独立部署一套后台服务,对于个人开发者尤其是客户端开发来说时间成本较高,后期维护也需要投入不少精力,最好还是使用已有的下发平台。
调研了一下发现目前Pushy和Shiply平台都支持RN热更新。
Pushy 是ReactNative中文网推出的RN下发平台,支持差量更新,CLI和网页双端管理。
Shiply 是腾讯端服务团队推出的面向客户端的全场景发布平台,支持动态下发配置、资源、软件包(App)、Android热更新、flutter热更新,最近刚上线了RN热更新功能。
体验下来感觉Shiply的前端交互更友好,有完整的审批发布流程,支持多维度的下发规则,能看到客户端下发加载数据,下面详细介绍下Shiply RN热更新的接入和使用过程。
2. Shiply RN热更新接入实战
2.1. 创建Shiply项目及产品
参考第三点中的发布平台使用指引,创建一个测试项目,并创建Android和iOS产品。
2.2. 创建RN模块
参考第三点中的发布平台使用指引,创建一个名叫「testRN」的模块,绑定2.1中创建的两个产品。
2.3. 接入Shiply RN动态化SDK
2.3.1. 创建RN demo
先创建一个RN demo工程:
npx @react-native-community/cli init TestShiplyRN --version 0.78
2.3.2. 添加RN层依赖
在demo项目根目录执行
npm install rn-shiply-upgrade
2.3.3. 添加Android层依赖
在项目Android目录下的build.gradle文件中添加maven地址
allprojects {
repositories {
maven { url "https://tencent-tds-maven.pkg.coding.net/repository/shiply/repo" }
}
}
2.3.4. 添加热更新检查与加载代码
2.3.4.1. 修改RN层代码
import React from 'react';
import {Platform, Text, View} from 'react-native';
import { HotUpdateHelper, HotUpdateButton } from 'rn-shiply-upgrade'; // 新增导入辅助类
const App = () => {
// 初始化热更新配置(只需一次)
HotUpdateHelper.getInstance({
// 需要修改为业务方自己的android/ios appId和appKey
appId: Platform.OS === 'ios' ? 'iOSAppId' : 'androidAppId',
appKey: Platform.OS === 'ios'
? 'iOSAppKey'
: 'androidAppKey',
deviceId: '33333', // 需要修改为实际的设备id
appVersion: "1.0.0", // 应用版本号
shiplyResName: 'testRN', // 资源key名称,对应shiply平台上中创建的RN模块名字
});
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text> 热更新测试: 当前是本地内容</Text>
<View style={{ height: 10 }} />
{/* 默认样式按钮 */}
<HotUpdateButton />
</View>
);
};
export default App;
androidAppId/androidAppKey/iOSAppId/iOSAppKey要改成2.1中创建的Android/iOS产品的真实appId/appKey。
2.3.4.2. 修改Android层代码
class MainApplication : Application(), ReactApplication {
override val reactNativeHost: ReactNativeHost =
object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
}
override fun getJSMainModuleName(): String = "index"
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
override fun getJSBundleFile(): String? {
var result: String? = null
// 从shiply获取RN bundle路径,业务方需要将资源key修改为shiply平台中创建的资源key名称
result = ShiplyReactNativeUpgradeModule.getJSBundleFilePath(applicationContext as Application, "testRN")
Log.d("MainApplication", "getJSBundleFile: $result")
return result
}
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
}
override val reactHost: ReactHost
get() = getDefaultReactHost(applicationContext, reactNativeHost)
override fun onCreate() {
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
load()
}
}
}
复写getJSBundleFile方法,从shiply获取bundle路径。
2.3.4.3. 修改iOS层代码
class AppDelegate: RCTAppDelegate {
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
self.moduleName = "TestShiplyRN"
self.dependencyProvider = RCTAppDependencyProvider()
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = [:]
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
override func sourceURL(for bridge: RCTBridge) -> URL? {
self.bundleURL()
}
override func bundleURL() -> URL? {
// 业务方需要将资源key修改为shiply平台中创建的资源key名称
ShiplyReactNativeUpgradeUtil.getJSBundleFilePath("testRN");
if let path = ShiplyReactNativeUpgradeUtil.getJSBundleFilePath("testRN") {
NSLog("bundleURL called,path = %@ ", path);
return URL(fileURLWithPath: path)
}
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
}
}
复写bundleURL方法,从shiply获取bundle路径。
做完以上步骤后就完成了接入。
这时点击检查更新没有什么效果,因为还没有在shiply配置新版本RN热更新产物。
2.4. 打包RN动态化产物
2.4.1. 修改Demo测试代码
import React from 'react';
import {Platform, Text, View} from 'react-native';
import { HotUpdateHelper, HotUpdateButton } from 'rn-shiply-upgrade'; // 新增导入辅助类
const App = () => {
// 初始化热更新配置(只需一次)
HotUpdateHelper.getInstance({
// 需要修改为业务方自己的android/ios appId和appKey
appId: Platform.OS === 'ios' ? 'iOSAppId' : 'androidAppId',
appKey: Platform.OS === 'ios'
? 'iOSAppKey'
: 'androidAppKey',
deviceId: '33333', // 需要修改为实际的设备id
appVersion: "1.0.0", // 应用版本号
shiplyResName: 'testRN', // 资源key名称,对应shiply平台上中创建的RN模块名字
});
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/*<Text> 热更新测试: 当前是本地内容</Text>*/}
<Text> 热更新测试: 当前是热更新内容</Text>
<View style={{ height: 10 }} />
{/* 默认样式按钮 */}
<HotUpdateButton />
</View>
);
};
export default App;
主要修改下UI文案,区分本地版本和热更新版本。
2.4.2. 打包Android产物
在工程的根目录输入如下命令进行打包:
react-native bundle --entry-file ./index.js --bundle-output ./bundle/androidBundle/index.android.bundle --platform android --assets-dest ./bundle/androidBundle --dev false
执行后,工程根目录/bundle/androidBundle下会生成Android产物,全选androidBundle下所有文件后进行压缩,得到的zip文件将用于上传到shiply。
2.4.3. 打包iOS产物
在工程的根目录输入如下命令进行打包:
react-native bundle --entry-file ./index.js --bundle-output ./bundle/iOSBundle/index.ios.bundle --platform ios --assets-dest ./bundle/iOSBundle --dev false
执行后,工程根目录/bundle/iOSBundle下会生成iOS产物,全选iOSBundle下所有文件后进行压缩,得到的zip文件将用于上传到shiply。
2.5. 发布RN动态化产物
参考3中的发布平台使用指引,上传2.4中的zip文件生成发布任务:
还原测试代码,在Demo Android目录执行./gradlew app:assembleRelease编译release apk,安装后运行九能拉到远端的RN产物了。
这里按钮和弹框都是shiply sdk提供的默认UI,也可以参考SDK中的HotUpdateButton 和HotUpdateHelper来自定义UI和请求时机。
安装加载成功后,shiply前端页面可以看到相关的数据上报。
2.6. 小结
总体体验下来接入比较顺畅,花个半天时间就能正常接入shiply rn热更新。因为CodePush下架急需替代方案的开发者可以考虑接入试用下。
掘金图片上传一直失败,也可以看下这篇文章:blog.csdn.net/sckgenius/a…
3. 参考文档