【React Native】差量热更新(一)

本文介绍了一个基于React Native的热更新方案,通过自定义封装实现一键生成diff包,简化了更新流程。文章详细展示了生成diff-bundle的具体步骤,包括清理、打包、对比、压缩等过程。

首先感谢React Native 实现热部署、差异化增量热更新提供的方案,我只是在这个方案的基础上封装了一层实用一点的diff一键生成,便于提供diff包。
OK,一言不合先来段代码。下面的代码片段是生成diff-bundle的总体流程。

public void windowsDiffCreator(String desDir,String sourceProjectDir) {
        //根据根路径 创建对应的文件夹
        prepareDir(desDir);
        //清空newDir下的文件
        deleteNewDirChildFiles();
        //执行bundle打包命令
        doReactNativeBundle(desDir, sourceProjectDir);
        //与old进行diff算法
        //将diff目录下的文件都删除掉
        deleteDiffDirChildFiles();
        //生成diff所有文件
        createDiff();
        //删除old文件夹下所有资源
        deleteOldDirChildFiles();
        //将new文件夹下的所有资源copy到old中
        copyNew2Old();
        //压缩diff文件夹到rootDir中
        ZipUtils.zipMultiFile(getDiffFileDir(),getRootDir()+"/diff.zip",false);
    }

根据外部传入的desDir(生成diff相关的root目录)和sourceProjectDir(project所在的目录,目的是调用bundle打包的命令)执行以下几步操作:
1、准备工作:在desDir目录下,创建如下几个文件夹
(1)、new:bundle打包命令执行后的目标路径,里面会有bundle文件、全量的资源文件
(2)、old:上一次执行完bundle打包命令后的bundle文件和全量的资源文件,目的是用作diff-bundle时的参考对比目录
(3)、diff:new和old两个文件夹对比后,生成的差量文件夹,里面存放差量的diff-bundle文件和资源的所有差量文件
2、删除new目录下的所有文件,打包前先清空new路径下的所有文件
3、执行bundle的打包命令:执行cmd命令,进入到当前project所在路径,然后执行react-native bundle命令,执行bundle打包命令
4、将生成出来的new文件夹和old文件夹进行对比,生成diff文件夹
(1)、使用google提供的diff_match_patch,对bundle进行diff处理
(2)、对全量的资源进行diff算法,目前有局限性:新的资源文件名称必须区别于旧的资源文件名称,即不能出现覆盖文件的现象
5、生成diff文件完毕后,将old文件夹清空,并将new的文件夹下所有文件copy到old文件夹下
6、对diff文件夹进行压缩处理,该压缩文件就是被放到server端,供用户下载的差量bundle文件
7、对生成的diff.zip文件进行MD5计算,生成MD5.txt,放到server端,供用户下载判断是否需要下载增量diff.zip包

下面着重介绍一下3、4两步。
先看下第三步的核心代码:

    private static String BUNDLE_CMD = "cmd /c react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output %s/bundle/new/index.android.bundle --assets-dest %s/bundle/new/res/";//打bundle文件的StringFormat,目前是使用默认的index.android.js默认名称 后续可以由用户自主选择名称进行打包
    private static String CD_SOURCE_PROJECT_DIR = "cmd /k cd %s";//进入项目的根路径 用于执行打包命令的StringFormat
    private static String CD_DISK = "cmd /k %s";//切换磁盘符的StringFormat
private void doReactNativeBundle(String desDir, String sourceProjectDir) {
        //由于在windows平台下进行的,首先获取磁盘符 如D:
        String diskDir = desDir.substring(0,2);
        System.out.println(diskDir);
        //Format切换磁盘符的标准字符串
        String cmdDisk = String.format(CD_DISK,diskDir);
        //开启cmd process,准备执行部分命令
        Runtime runtime = Runtime.getRuntime();
        //Format进入到项目根路径命令
        String cdCMD = String.format(CD_SOURCE_PROJECT_DIR,sourceProjectDir);
        //Format执行打包命令
        String bundleCMD = String.format(BUNDLE_CMD,desDir,desDir);
        System.out.println(cdCMD);
        System.out.print(bundleCMD);
        try {
            runtime.exec(cmdDisk);//执行切换磁盘符命令
            runtime.exec(cdCMD);//执行进入到项目根路径命令
            Process p = runtime.exec(bundleCMD);//执行打bundle文件命令
            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream(), "GBK"));
            String line = null;
            while ((line = input.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

接下来再看一下第四步(将新打出来的bundle文件和资源文件与上一版本的bundle文件和资源文件进行diff运算)的核心代码:

private void createDiff() {
        //(1)、先生成diff-bundle文件到diff下
        File newBundleFile = new File(getNewFileDir(),INDEX_ANDROID_BUNDLE);
        if (!newBundleFile.exists()) {
            System.out.println("新的bundle文件没有生成");
            return;
        }
        File oldBundleFile = new File(getOldFileDir(),INDEX_ANDROID_BUNDLE);
        File diffBundleFile = new File(getDiffFileDir(),INDEX_ANDROID_BUNDLE);
        if (!oldBundleFile.exists()) {
            //没有旧的bundle文件 创建一个空的bundle文件 进行diff处理
//            FileUtils.copyFile(newBundleFile.getAbsolutePath(),diffBundleFile.getAbsolutePath());
            try {
                oldBundleFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //存在旧的bundle文件 进行diff算法进行处理
        new BundleDiffCreator().createDiffBundle(oldBundleFile.getAbsolutePath(),newBundleFile.getAbsolutePath(),diffBundleFile.getAbsolutePath());
        //(2)、生成资源的diff文件
        File newResFile = new File(getNewFileDir(),RES_FILE_DIR_NAME);
        File oldResFile = new File(getOldFileDir(),RES_FILE_DIR_NAME);
        File diffResFile = new File(getDiffFileDir(),RES_FILE_DIR_NAME);
        if (!oldResFile.exists() || oldResFile.listFiles().length == 0) {
            //如果没有旧的资源文件 直接把新的资源文件夹都copy到diff下
            FileUtils.copyDir(newResFile.getAbsolutePath(),diffResFile.getAbsolutePath());
        } else {
            //如果存在旧的资源文件 进行diff运算 将不同的copy过去
            for (File newRatioFile:newResFile.listFiles()) {
                if (newRatioFile.isDirectory()) {
                    //100%是个directory 如mhdpi,xmhdpi......
                    File oldRatioFile = new File(oldResFile,newRatioFile.getName());
                    if (!oldRatioFile.exists()) {
                        oldRatioFile.mkdirs();
                    }
                    File[] oldRatioChildFiles = oldRatioFile.listFiles();
                    List<String> childFileNameList = new ArrayList<>();
                    for (File oldRatioChildFile:oldRatioChildFiles) {
                        childFileNameList.add(oldRatioChildFile.getName());
                    }
                    for (File newRatioChildFile : newRatioFile.listFiles()) {
                        if (childFileNameList.contains(newRatioChildFile.getName())) {
                            //如果包含 代表有这个文件 因为目前不允许名称相同的覆盖文件
                            continue;
                        } else {
                            File diffRatioFile = new File(diffResFile,newRatioFile.getName());
                            if (!diffRatioFile.exists()) {
                                diffRatioFile.mkdirs();
                            }
                            File diffChildFile = new File(diffRatioFile,newRatioChildFile.getName());
                            FileUtils.copyFile(newRatioChildFile.getAbsolutePath(),diffChildFile.getAbsolutePath());
                        }
                    }
                }
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值