Android Transform增量编译

简介


这个文章的基础是你基本已经完成了transfrom 的开发了,而后你碰到了编译速度慢的问题。
在Transform的抽象类中有一个isIncremental方法,这个方法就表明着是否开启增量编译。

ide

增量编译定义

编译过程当中会去遍历全部的jar .class文件,而后对文件进行io操做,以及asm插入代码,这个过程耗时通常都会很长。

这里须要注意一点:不是每次的编译都是能够怎量编译的,毕竟一次clean build彻底没有增量的基础,因此,咱们须要检查当前的编译是否增量编译。
须要作区分:

不是增量编译,则清空output目录,而后按照前面的方式,逐个class/jar处理
增量编译,则要检查每一个文件的Status,Status分为四种,而且对四种文件的操做不尽相同

NOTCHANGED 当前文件不须要处理,甚至复制操做都不用
ADDED、CHANGED 正常处理,输出给下一个任务
REMOVED 移除outputProvider获取路径对应的文件

上述是对增量的一些定义,能够看出来在transfrom过程当中,应该是对文件打了一些tag标签。
那么咱们在开发阶段首先要先区分当前此次是否是增量编译,而后再编译当前变动的文件,对变动的文件进行处理。

优化

代码分析


我在代码设计中,对transform进行了一次代码抽象,把文件操做进行了一次抽象,同时把扫描以及.class文件操做进行了一些基础封装,后续的开发就能够直接在这个的基础上进行后续快速迭代开发。
ui

public void startTransform() {
        try {
            if (!isIncremental) {
                outputProvider.deleteAll();
            }
            for (TransformInput input : inputs) {
                for (JarInput jarInput : input.getJarInputs()) {
                    Status status = jarInput.getStatus();
                    String destName = jarInput.getFile().getName();
                    /* 重名名输出文件,由于可能同名,会覆盖*/
                    String hexName = DigestUtils.md5Hex(jarInput.getFile().getAbsolutePath()).substring(0, 8);
                    if (destName.endsWith(".jar")) {
                        destName = destName.substring(0, destName.length() - 4);
                    }
                    /*得到输出文件*/
                    File dest = outputProvider.getContentLocation(destName + "_" + hexName,
                            jarInput.getContentTypes(), jarInput.getScopes(), Format.JAR);
                    if (isIncremental) {
                        switch (status) {
                            case NOTCHANGED:
                                break;
                            case ADDED:
                                foreachJar(dest, jarInput);
                                break;
                            case CHANGED:
                                diffJar(dest, jarInput);
                                break;
                            case REMOVED:
                                try {
                                    deleteScan(dest);
                                    if (dest.exists()) {
                                        FileUtils.forceDelete(dest);
                                    }
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                        }
                    } else {
                        foreachJar(dest, jarInput);
                    }
                }
                for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
                    foreachClass(directoryInput);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
复制代码


我在遍历循环jar,开始的时候咱们先判断当前此次是否是增量编译,若是不是增量则开始遍历全部jar,若是是增量编译,会去获取当前jar的状态,若是状态是删除则先扫描jar以后把output 中的文件删除。若是状态是ADD的状况下,则扫描修改这个jar文件。最后若是是CHANGE状态,则先扫描新久两个jar,比较获取删除的文件,而后重复ADD操做。
spa

private void foreachClass(DirectoryInput directoryInput) throws IOException {
        File dest = outputProvider.getContentLocation(directoryInput.getName(), directoryInput.getContentTypes(),
                directoryInput.getScopes(), Format.DIRECTORY);
        Map<File, Status> map = directoryInput.getChangedFiles();
        File dir = directoryInput.getFile();
        if (isIncremental) {
            for (Map.Entry<File, Status> entry : map.entrySet()) {
                Status status = entry.getValue();
                File file = entry.getKey();
                String destFilePath = file.getAbsolutePath().replace(dir.getAbsolutePath(), dest.getAbsolutePath());
                File destFile = new File(destFilePath);
                switch (status) {
                    case NOTCHANGED:
                        break;
                    case ADDED:
                    case CHANGED:
                        try {
                            FileUtils.touch(destFile);
                        } catch (Exception ignored) {
                            Files.createParentDirs(destFile);
                        }
                        modifySingleFile(dir, file, dest);
                        break;
                    case REMOVED:
                        Log.info(entry);
                        deleteDirectory(destFile, dest);
                        break;
                }
            }
        } else {
            changeFile(dir, dest);
        }
    }
复制代码


这个是修改.class文件的操做 , 和修改jar包的逻辑基本同样,可是又一个区别,若是是增量编译的状况下,咱们获取的对象是一个Map,而非增量编译的状况下,咱们使用的是整个文件夹路径。
设计

结尾

咱们的任务名DoubleTabTransform
这是一次全量编译的耗时
3d

image.png

这是一次增量编译的耗时
image.png
最后咱们对与耗时进行了一次分析,若是在二次编译的状况下,咱们将2800毫秒优化到了 68毫秒。
相关文章
相关标签/搜索