react native 已经集成好的热更新方案:codePush
自定义热更新实现逻辑:其实就是修改bundlejs
Android 具体实现:
1.在rn目录下运行命令,打包 bundlejs 和静态文件,打包之后需要将文件进行压缩上传。
react-native bundle --platform android --dev false --entry-file index.js --bundle-output ./bundle/android/index.android.bundle --assets-dest ./bundle/android
注意, ./bundle/android 这个文件是自定义的,你想保存到那里就去修改。(注意:需要先创建文件夹,不然会报错)
2.在 android 文件夹中,找到 MainApplication.java 这个文件,在 ReactNativeHostWrapper 中添加入这段代码,像相当于重写了 getJSBundleFile 这个方法。(这一步相当于替换运行的bundlejs)
@Override
protected String getJSBundleFile() {
// 检查本地bundle文件是否存在
String localBundlePath = new File(getFilesDir(), "android/index.android.bundle").getAbsolutePath();
File bundleFile = new File(localBundlePath);
// 如果本地bundle文件存在,则使用它
if (bundleFile.exists()) {
return bundleFile.getAbsolutePath();
} else {
// 如果本地bundle不存在,则使用默认bundle
return super.getJSBundleFile();
}
}
3.在rn代码中找到合适的位置下载并且替换bundlejs文件,代码大概如下(这一步是下载并且解压代码)
//下载并替换bundle
export const downloadAndReplaceBundle = async (updateInfo) => {
const libraryPath = `${IS_ANDROID ? RNFetchBlob.fs.dirs.DocumentDir : RNFetchBlob.fs.dirs.LibraryDir}`;
const dirName = `${IS_ANDROID ? 'android' : 'ios'}`;
try {
const path = `${libraryPath}`;
const zipPath = `${path}/bundlezip`;
let zipRes;
zipRes = await RNFetchBlob.config({
path: zipPath, // 指定保存路径
fileCache: true, // 启用文件缓存
timeout: 120000, // 设置超时时间
}).fetch('GET', getFileUrl(updateInfo?.hotUrl)); // 使用 GET 请求下载文件
//解压
await unzip(zipRes.path(), path);
// 重启
setTimeout(() => {
RNRestart.Restart();
}, 1000);
} catch (error) {
}
};
这里我用到了
rn-fetch-blob react-native-zip-archive react-native-restart
这三个包,分别是下载,解压,重启应用的功能。
解压到你能找的目录就行,然后在原生的android代码中也要读取这个目录下的bundlejs。
然后 android 的热更新就大功告成了。
Ios具体实现
其实rn的代码都是这样做,不通过的地方就是需要在 ios 原生代码中读取相应的 bundlejs
首先是打包 ios的bundlejs
react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ./bundle/ios/main.jsbundle --assets-dest ./bundle/ios
同样的,打包后的文件的位置是自定义的。
然后就是原生iOS代码中替换bundlejs
在 ios 文件夹中找到 AppDelegate.m 或者 AppDelegate.mm 这文件,然后找到这段代码
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
直接把 else 后面的代码去掉,修改成
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
// 检查本地bundle文件是否存在
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"jsbundle"];
NSFileManager *fileManager = [NSFileManager defaultManager];
// // 获取Library目录下的bundle文件路径
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
NSString *localBundlePath = [libraryPath stringByAppendingPathComponent:@"ios/main.jsbundle"];
// 如果本地bundle存在,使用本地bundle
if ([fileManager fileExistsAtPath:localBundlePath]) {
NSLog(@"存在本地bundle,使用本地bundle");
return [NSURL fileURLWithPath:localBundlePath];
}
NSLog(@"不存在本地bundle,使用默认bundle");
// 否则使用默认bundle
return [NSURL fileURLWithPath:bundlePath];
#endif
然后就大功告成了。
总结:整体流程就是 下载-解压-替换,就完成热更新了。
ps:这个只是简易的实现,后续可能还要考虑到,热更完成之后,出现错误需要回滚等等等等的事情。这个我会在下一篇文章中说一下我目前这个项目的处理。