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

来源:互联网 发布:剑三dbm数据怎么删除 编辑:程序博客网 时间:2024/05/20 12:46

热更新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数组。这个过程随后会补图说明。

0 0
原创粉丝点击