ReactNative热更新解决方案(包含插件)

新做了一个ReactNative的项目,中文网的热更新不太符合我的业务需求,所以自己上手做了一个~~~~

一、原理

热更新原理很简单,RN提供了getJSBundleFile方法,此方法返回一个路径,路径里包含代码构建合并的index.android.bundle文件和资源文件(drawable开头的)。所以将已经下载好的文件路径返回,再重启APP就完成了热更新。

 

二、实践

今天把热更新所有的代码打包成了一个插件,希望能帮助到各位。

插件中有些代码来源于网上各个大佬,如有雷同,我只能说声感谢!

git

https://github.com/Txiaomo/react-native-hot-deployment

npm

https://www.npmjs.com/package/react-native-hot-deployment

 

完整实践如下

 1.首先应用进行插件安装,git上有安装步骤及相关方法。

 2.在项目package.json文件里面配置

"package:hotpushFile": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output bundle_zip/index.android.bundle --assets-dest bundle_zip"

在项目根目录新建文件夹bundle_zip   并不一定要这个命名,但是命名要与上图中新加的命令里面的名字保持一致

热更新的时候运行 npm run package:hotpushFile 这样编译过后的js文件与项目资源文件就是被放进bundle_zip文件里,然后我们需要将文件夹压缩为bundle.zip (此命名唯一,需要更改命名请在插件的Download文件里面自行修改) 压缩的目的是为了下载的资源包更小

 我这里是使用jenkins构建的,所以压缩文件的步骤是由运维帮忙完成的,压缩完成后放在服务器一个固定的目录。运维帮忙创建了这个文件的下载链接。我们可以在项目里将下载地址写死

2.需要后端配置接口如下 

因为我们app提示升级需要告知用户升级的内容,所以我们一般会选择用一个前端页面来维护app的升级管理。前端页面如下

这样我们app在启动后或者登录时就可以请求后端接口,将接口给的app版本与当前版本作对比来查看app是否需要升级!

代码如下

MainApplication中

import android.content.SharedPreferences;

import android.content.pm.PackageInfo;

import android.content.pm.PackageManager;

import java.io.File;

public class MainApplication extends Application implements ReactApplication {

    private SharedPreferences sharedPreferences;

    private String newVersion = "";

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {

        @Override

        protected String getJSBundleFile() {

            PackageManager pm = getApplicationContext().getPackageManager();

            PackageInfo pi;

            try {

                pi = pm.getPackageInfo(getApplicationContext().getPackageName(), 0);

                newVersion = pi.versionName;

            } catch (PackageManager.NameNotFoundException e) {

                e.printStackTrace();

            }

        sharedPreferences=getSharedPreferences("appVersion.xml",MODE_PRIVATE);

        String oldVersion=sharedPreferences.getString("version","0.0.0");

        String jsBundleFile = getFilesDir().getAbsolutePath()+"/index.android.bundle";

        File file = new File(jsBundleFile);

        int oldVersionBack=Integer.parseInt(oldVersion.replaceAll("\\.",""));

        int newVersionBack=Integer.parseInt(newVersion.replaceAll("\\.",""));

        if((oldVersion.equals("0.0.0")|| oldVersionBack>=newVersionBack)&& file != null             && file.exists()) {

            return jsBundleFile;

        } else {

            return super.getJSBundleFile();

        }
    }
}

 

const { HotPushVersion, Download, DownloadApk, RestartApp } = NativeModules;

//根据后端接口返回类型来判断更新方式
if (hotpush) {
   /**
    * 下载zip热更新压缩包
    */
   Download.downloading(url);
   this.listener = DeviceEventEmitter.addListener("downloadZipStatus", e => {
     if (e && e.status === "success") {
         //下载并解压成功
       /**
        * version:更新的版本号
        */
       HotPushVersion.updataVersion(version);
        console.log("重启应用中");
       RestartApp.Restart();
     } else if (e && e.status === "error") {
         //下载失败
     } else if (e && e.status === "decompressionError") {
         //解压失败
     } else if (e && e.status) {
       console.log("下载进度====>" + e.status + "%");
     } else {
       console.log("=================>");
     }
   });
 } else {    
    /**     
     * 下载apk     
     * 接收两个参数,第一个为安装包下载的url,第二个为描述     
     */
    DownloadApk.downloading(url, "PDA"); 
 }

如有不懂的,欢迎提问

 

 

讨论。。。

之所以要打包图片资源文件,是因为返回了一个新的index.android.bundle文件路径的时候,RN默认会从这个路径读取图片,如果没有图片,那么app上也不会显示。上文中提供的插件有一个问题,我采用的方式是每次打包所有的资源文件,然后热更新的时候也是下载的所有资源文件。不过由于是zip压缩文件,其实也不会太大。不过我想找到一种每次更新只需要下载新图片的方式。路过的大神们有想法请留言...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章