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毫秒。
相關文章
相關標籤/搜索