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

首先感謝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());
                        }
                    }
                }
            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章