Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

热更新Tinker研究(八):res和so的patch文件生成

$
0
0

热更新Tinker研究(一):运行tinker-sample-android
热更新Tinker研究(二):结合源码学习Dex格式
热更新Tinker研究(三):加载补丁
热更新Tinker研究(四):TinkerLoader
热更新Tinker研究(五):Application的隔离
热更新Tinker研究(六):TinkerPatchPlugin
热更新Tinker研究(七):Dex的patch文件生成
热更新Tinker研究(八):res和so的patch文件生成
热更新Tinker研究(九):Dex文件的patch
热更新Tinker研究(十):Res文件的patch
热更新Tinker研究(十一):so文件的patch

热更新Tinker研究(八):res和so的patch文件生成

ResDiffDecoder和BsDiffDecoder分别是负责resource和so文件的patch生成相关的,它们很多地方比较相似,这里放在一起来说明。

一、ResDiffDecoder

ResDiffDecoder是控制resources的patch文件生成的,主要是控制增加、修改和删除的信息,这里对于大文件和小文件也有不同的区分,小文件只需要直接拷贝,大文件需要做差分文件。

public class ResDiffDecoder extends BaseDecoder {
    private static final String TEST_RESOURCE_NAME        = "only_use_to_test_tinker_resource.txt";
    private static final String TEST_RESOURCE_ASSETS_PATH = "assets/" + TEST_RESOURCE_NAME;

    private static final String TEMP_RES_ZIP  = "temp_res.zip";
    private static final String TEMP_RES_7ZIP = "temp_res_7ZIP.zip";
    private final InfoWriter                     logWriter;
    private final InfoWriter                     metaWriter;
    private       ArrayList<String>              addedSet;  //增加的
    private       ArrayList<String>              modifiedSet;   //修改的
    private       ArrayList<String>              largeModifiedSet;  ///修改的大文件
    private       HashMap<String, LargeModeInfo> largeModifiedMap;  //修改的大文件map保存
    private ArrayList<String> deletedSet;   //删除的
    ......
}

dealWithModeFile()是主要进行区分大小文件处理的方法,

    private boolean dealWithModeFile(String name, String newMd5, File oldFile, File newFile, File outputFile) throws IOException {
        if (checkLargeModFile(newFile)) {   //判断是否是大文件
            if (!outputFile.getParentFile().exists()) { //确保父目录存在
                outputFile.getParentFile().mkdirs();
            }
            BSDiff.bsdiff(oldFile, newFile, outputFile);    //生成diff文件
            //treat it as normal modify
            //检查是否需要当做diff文件对待
            if (Utils.checkBsDiffFileSize(outputFile, newFile)) {
                LargeModeInfo largeModeInfo = new LargeModeInfo();
                largeModeInfo.path = newFile;
                largeModeInfo.crc = FileOperation.getFileCrc32(newFile);
                largeModeInfo.md5 = newMd5;
                largeModifiedSet.add(name); //加入large信息
                largeModifiedMap.put(name, largeModeInfo);
                writeResLog(newFile, oldFile, TypedValue.LARGE_MOD);
                return true;
            }
        }
        modifiedSet.add(name);
        //将新的文件拷贝目标路径
        FileOperation.copyFileUsingStream(newFile, outputFile);
        writeResLog(newFile, oldFile, TypedValue.MOD);
        return false;
    }

这里会判断新的文件是否是大文件,如果是大文件,就先用BSDiff生成diff文件,如果判断diff文件符合要求,就按照大文件进行处理。
对于大文件的判断,

    private boolean checkLargeModFile(File file) {
        long length = file.length();
        if (length > config.mLargeModSize * TypedValue.K_BYTES) {
            return true;
        }
        return false;
    }

对于是否需要按照大文件处理,

    public static boolean checkBsDiffFileSize(File bsDiffFile, File newFile) {
        if (!bsDiffFile.exists()) {
            throw new TinkerPatchException("can not find the bsDiff file:" + bsDiffFile.getAbsolutePath());
        }

        //check bsDiffFile file size
        double ratio = bsDiffFile.length() / (double) newFile.length();
        if (ratio > TypedValue.BSDIFF_PATCH_MAX_RATIO) {
            //如果diff比newFile的0.8倍还大,按照普通文件处理
            Logger.e("bsDiff patch file:%s, size:%dk, new file:%s, size:%dk. patch file is too large, treat it as newly file to save patch time!",
                bsDiffFile.getName(),
                bsDiffFile.length() / 1024,
                newFile.getName(),
                newFile.length() / 1024
            );
            return false;
        }
        return true;
    }

二、BsDiffDecoder

BsDiffDecoder是负责os文件差分的,区别是没有resources那种大小文件的区分,关键性代码如下,

 public boolean patch(File oldFile, File newFile) throws IOException, TinkerPatchException {
        //first of all, we should check input files
        if (newFile == null || !newFile.exists()) {
            return false;
        }
        //new add file
        String newMd5 = MD5.getMD5(newFile);
        File bsDiffFile = getOutputPath(newFile).toFile();

        if (oldFile == null || !oldFile.exists()) {
            FileOperation.copyFileUsingStream(newFile, bsDiffFile);
            writeLogFiles(newFile, null, null, newMd5);
            return true;
        }

        //both file length is 0
        if (oldFile.length() == 0 && newFile.length() == 0) {
            return false;
        }
        if (oldFile.length() == 0 || newFile.length() == 0) {
            FileOperation.copyFileUsingStream(newFile, bsDiffFile);
            writeLogFiles(newFile, null, null, newMd5);
            return true;
        }

        //new add file
        String oldMd5 = MD5.getMD5(oldFile);

        if (oldMd5.equals(newMd5)) {
            return false;
        }

        if (!bsDiffFile.getParentFile().exists()) {
            bsDiffFile.getParentFile().mkdirs();
        }
        BSDiff.bsdiff(oldFile, newFile, bsDiffFile);

        if (Utils.checkBsDiffFileSize(bsDiffFile, newFile)) {
            writeLogFiles(newFile, oldFile, bsDiffFile, newMd5);
        } else {
            FileOperation.copyFileUsingStream(newFile, bsDiffFile);
            writeLogFiles(newFile, null, null, newMd5);
        }
        return true;
    }

三、bsdiff算法

bsdiff算法的核心思想是构造两个数组,找出公共序列的位置,一个是diff数组,一个是extra数组。diff数组是两个序列之间的差,extra表示new多余old的部分

求公共子串,需要用到后缀数组,这里关于关于后缀数组,做几个基本的名词的解释。

字符串的大小比较: 关于字符串的大小比较,是指通常所说的 “ 字典顺序 ” 比较, 也就是对于两个字符串 u 、v ,令 i 从 1 开始顺次比较 u[i] 和 v[i] ,如果u[i]=v[i] 则令 i 加 1 ,否则若 u[i] < v[i] 则认为 u < v ,u[i] > v[i] 则认为 u > v,比较结束。如果 i > len(u) 或者 i > len(v) 仍比较不出结果,那么若 len(u) < len(v)则认为 u < v , 若 len(u)=len(v) 则 认 为 u=v ,若 len(u) > len(v) 则 u > v 。
注:从字符串的大小比较的定义看,字符串s的所有后缀中任其中一对(u,v)不可能会相等,因为必要条件 len(u) ≠ len(v)不可能满足。所以任一字符串s中有len(s)个互不相同的后缀。我们可以将s的所有后缀排列,利用 后缀数组sa 与 名次数组rank 储存。

后缀数组sa: 将s的n个后缀从小到大排序后将 排序后的后缀的开头位置 顺次放入sa中,则sa[i]储存的是排第i大的后缀的开头位置。简单的记忆就是“排第几的是谁”。

名次数组rank: rank[i]保存的是suffix(i){后缀}在所有后缀中从小到大排列的名次。则 若 sa[i]=j,则 rank[j]=i。简单的记忆就是“你排第几”。

enter description here

enter description here

然后再根据公共子串的位置去得到diff和extra数组。这个过程随后会补图说明。

作者:huweigoodboy 发表于2017/4/20 14:25:21 原文链接
阅读:43 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>