深刻探索 Gradle 自動化構建技術(6、Gradle 插件平臺化框架 ByteX 探祕之旅)

前言

成爲一名優秀的Android開發,須要一份完備的知識體系,在這裏,讓咱們一塊兒成長爲本身所想的那樣~。

1、前置知識

一、函數式編程

1)、什麼是函數式編程?

面向對象編程是對數據進行抽象,而函數式編程是對行爲進行抽象。現實世界中,數據和行爲並存,而程序也是如此。java

2)爲何要學習函數式編程?

  • 用函數(行爲)對數據處理,是學習大數據的基石。
  • 好的效率(併發執行),
  • 完成一個功能使用更少的代碼。
  • 對象轉向面向函數編程的思想有必定難度,須要大量的練習

二、Java 1.8 Lambda 表達式

1)、什麼是 Lambda 表達式?

Lambda 是一個匿名函數,即沒有函數名的函數,它簡化了匿名委託的使用,讓代碼更加簡潔。python

2)、兩個簡單的 Lambda 表達式示例

//匿名內部類
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.print("hello toroot");
    }
};

//lambda
Runnable r2 = ()->System.out.print("hello toroot");

//匿名內部類
TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return Long.compare(o1.length(),o2.length());
    }
});

//lambda
TreeSet<String> ts2 = new TreeSet<>((o1,o2)-> Long.compare(o1.length(),o2.length()));
複製代碼

3)、Lambda 表達式語法

Lambda 表達式在 Java 語言中引入了一個新的語法元素和操做符。這個操做符爲 「->」 ,該操做符被稱爲 Lambda 操做符或剪頭操做符。android

它將 Lambda 分爲兩個部分:git

  • 左側:指定了 Lambda 表達式須要的全部參數
  • 右側:指定了 Lambda 體,即 Lambda 表達式要執行的功能

4)、Lambda 表達式語法格式

  • 一、無參數,無返回值: () -> System.out.println("Hello Lambda!");
  • 二、有一個參數,而且無返回值: (x) -> System.out.println(x)
  • 三、若只有一個參數,小括號能夠省略不寫: x -> System.out.println(x)
  • 四、有兩個以上的參數,有返回值,而且 Lambda 體中有多條語句:
Comparator<Integer> com = (x, y) -> {
			System.out.println("函數式接口");
			return Integer.compare(x, y);
		};
複製代碼
  • 五、若 Lambda 體中只有一條語句, return 和 大括號 均可以省略不寫: Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
  • 六、Lambda 表達式的參數列表的數據類型能夠省略不寫,由於 JVM 編譯器能夠經過上下文推斷出數據類型,即「類型推斷」: (Integer x, Integer y) -> Integer.compare(x, y);

5)、函數式接口

Lambda 表達式須要「函數式接口」的支持。函數式接口即 接口中只有一個抽象方法的接口,稱爲函數式接口。 可使用註解 @FunctionalInterface 修飾,它能夠檢查是不是函數式接口。函數式接口的使用示例以下所示:程序員

@FunctionalInterface
public interface MyFun {
    public double getValue();
}

@FunctionalInterface
public interface MyFun<T> {
    public T getValue(T t);
}

public static void main(String[] args) {
    String newStr = toUpperString((str)->str.toUpperCase(),"toroot");
    System.out.println(newStr);
}

public static String toUpperString(MyFun<String> mf,String str) {
    return mf.getValue(str);
}
複製代碼

6)、Java 內置函數式接口

接口 參數 返回類型 示例
Predicate T boolean 這道題對了嗎?
Consumer T void 輸出一個值
Function<T,R> T R 得到 Person對象的名字
Supplier None T 工廠方法
UnaryOperator T T 邏輯非 (!)
BinaryOperator (T, T) T 求兩個數的乘積 (*)

7)、方法引用

當要傳遞給 Lambda 體的操做,已經有實現的方法了,可使用方法引用。方法引用使用 操做符 「 ::」 將方法名和對象或類的名字分隔開來。它主要有以下 三種 使用狀況 :github

  • 對象 :: 實例方法
  • 類 :: 靜態方法
  • 類 :: 實例方法

8)、何時能夠用 :: 方法引用(重點)

在咱們使用 Lambda 表達式的時候,」->」 右邊部分是要執行的代碼,即要完成的功能,能夠把這部分稱做 Lambda 體。有時候,當咱們想要實現一個函數式接口的那個抽象方法,可是已經有類實現了咱們想要的功能,這個時候咱們就能夠用方法引用來直接使用現有類的功能去實現。示例代碼以下所示:算法

Person p1 = new Person("Av",18,90);
        Person p2 = new Person("King",20,0);
        Person p3 = new Person("Lance",17,100);
        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);

        // 這裏咱們須要比較 list 裏面的 person,按照年齡排序
        // 那麼咱們最多見的作法是
        // sort(List<T> list, Comparator<? super T> c)
        // 一、由於咱們的 sort 方法的第二個參數是一個接口,因此咱們須要實現一個匿名內部類
        Collections.sort(list, new Comparator<Person>() {
            @Override
            public int compare(Person person1, Person person2) {
                return person1.getAge().compareTo(person2.getAge());
            }
        });
        // 二、由於第二個參數是一個 @FunctionalInterface 的函數式接口,因此咱們能夠用 lambda 寫法
        Collections.sort(list, (person1,person2) -> p1.getScore().compareTo(p2.getAge()));
        // 三、由於第二個參數咱們能夠用lambda的方式去實現,
        // 可是恰好又有代碼 Comparator.comparing 已經實現了這個功能
        // 這個時候咱們就能夠採用方法引用了
        /** * 重點: * 當咱們想要實現一個函數式接口的那個抽象方法,可是已經有類實現了咱們想要的功能, * 這個時候咱們就能夠用方法引用來直接使用現有類的功能去實現。 */
        Collections.sort(list, Comparator.comparing(Person::getAge));

        System.out.println(list);
複製代碼

其它 Java 內置的函數式接口示例以下所示:編程

public static void main(String[] args) {
    Consumer<String>  c = x->System.out.println(x);
    // 等同於
    Consumer<String> c2 = System.out::print;
}

public static void main(String[] args) {
    BinaryOperator<Double> bo = (n1,n2) ->Math.pow(n1,n2);
    BinaryOperator<Double> bo2 = Math::pow;
}

public static void main(String[] args) {
    BiPredicate<String,String> bp = (str1,str2) ->str1.equals(str2);
    BiPredicate<String,String> bp2 = String::equals;
}
複製代碼

注意:當須要引用方法的第一個參數是調用對象,而且第二個參數是須要引用方法的第二個參數(或無參數)時,使用 ClassName::methodNameapi

9)、構造器引用

格式: ClassName :: new 與函數式接口相結合,自動與函數式接口中方法兼容。 能夠把構造器引用賦值給定義的方法,可是構造器參數列表要與接口中抽象方法的參數列表一致。示例以下所示:數組

public static void main(String[] args) {
    Supplier<Person> x = ()->new Person();
    Supplier<Person> x2 = Person::new;
}

public static void main(String[] args) {
    Function<String,Person> f  = x->new Person(x);
    Function<String,Person> f2 = Person::new;
}
複製代碼

10)、數組引用

格式: type[] :: new,示例以下所示:

public static void main(String[] args) {
   Function<Integer,Person[]> f  = x->new Person[x];
   Function<Integer,Person[]>  f2 = Person[]::new;
}
複製代碼

三、Stream API

1)、Stream 是什麼?

Stream 是數據渠道,用於操做數據源(集合、數組等)所生成的元素序列。記住:「集合講的是數據,流講的是計算!」

2)、特色

  • 1)、Stream 本身不會存儲元素
  • 2)、Stream 不會改變源對象。相反,他們會返回一個持有結果的新 Stream
  • 3)、Stream 操做是延遲執行的。這意味着他們會等到須要結果的時候才執行

3)、一個 Stream 操做實例

取出全部大於18歲人的姓名,按字典排序,並輸出到控制檯,代碼以下所示:

private static  List<Person> persons = Arrays.asList(
            new Person("CJK",19,"女"),
            new Person("BODUO",20,"女"),
            new Person("JZ",21,"女"),
            new Person("anglebabby",18,"女"),
            new Person("huangxiaoming",5,"男"),
            new Person("ROY",18,"男")
    );
public static void main(String[] args) throws IOException {
 persons.stream().filter(x-    	>x.getAge()>=18).map(Person::getName).sorted().forEach(System.out::println);
}
複製代碼

4)、Stream 的操做三個步驟

  • 一、建立 Stream:一個數據源(如:集合、數組),獲取一個流
  • 二、中間操做:一箇中間操做鏈,對數據源的數據進行處理
  • 三、終止操做(終端操做):一個終止操做,執行中間操做鏈,併產生結果

一、建立 Steam

建立流主要有四種方式,其示例代碼以下所示:

@Test
public void test1(){
    //1. Collection 提供了兩個方法 stream() 與 parallelStream()
    List<String> list = new ArrayList<>();
    Stream<String> stream = list.stream(); //獲取一個順序流
    Stream<String> parallelStream = list.parallelStream(); //獲取一個並行流

    //2. 經過 Arrays 中的 stream() 獲取一個數組流
    Integer[] nums = new Integer[10];
    Stream<Integer> stream1 = Arrays.stream(nums);

    //3. 經過 Stream 類中靜態方法 of()
    Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
    
    //4. 建立無限流
    //迭代
    Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
    stream3.forEach(System.out::println);

    //生成
    Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
    stream4.forEach(System.out::println);
}
複製代碼

二、中間操做

  • 1)、篩選與切片
    • filter:接收 Lambda ,從流中排除某些元素
    • limit:截斷流,使其元素不超過給定數量
    • skip(n):跳過元素,返回一個扔掉了前 n 個元素的流。若流中元素不足 n 個,則返回一個空流。與 limit(n) 互補
    • distinct:篩選,經過流所生成元素的 hashCode() 和 equals() 去除重複元素
  • 2)、映射
    • map:接收 Lambda ,將元素轉換成其餘形式或提取信息。接收一個函數做爲參數,該函數會被應用到每一個元素上,並將其映射成一個新的元素,相似於 python、go 的 map 語法
    • flatMap:接收一個函數做爲參數,將流中的每一個值都換成另外一個流,而後把全部流鏈接成一個流
  • 3)、排序
    • sorted():天然排序
    • sorted(Comparator com):定製排序

這裏,咱們給出一些常見的使用示例,以下所示:

一、有個數組 Integer[] ary = {1,2,3,4,5,6,7,8,9,10},取出中間的第三到第五個元素。
List<Integer> collect = Arrays.stream(ary).skip(2).limit(3).collect(Collectors.toList());
複製代碼
二、有個數組 Integer[] ary = {1,2,2,3,4,5,6,6,7,8,8,9,10},取出裏面的偶數,並去除重複。
List<Integer> list = Arrays.stream(ary).filter(x -> x % 2 == 0).distinct().collect(Collectors.toList());
Set<Integer> integerSet = Arrays.stream(ary).filter(x -> x % 2 == 0).collect(Collectors.toSet());
複製代碼
三、有個二維數組,要求把數組組合成一個一維數組,並排序(1,2,3,4,5……12)

Integer[][] ary = {{3,8,4,7,5}, {9,1,6,2}, {0,10,12,11} };

Arrays.stream(ary).flatMap(item->Arrays.stream(item)).sorted().forEach(System.out::println);
複製代碼

3)、終止操做

終端操做會從流的流水線生成結果。其結果能夠是任何不是流的值,例如:List、Integer,甚至是 void 。

一、查找與匹配
接口 說明
allMatch(Predicate p) 檢查是否匹配全部元素
anyMatch(Predicate p) 檢查是否至少匹配一個元素
noneMatch(Predicate p) 檢查是否沒有匹配全部元素
findFirst() 返回第一個元素
findAny() 返回當前流中的任意元素
count() 返回流中元素總數
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 迭代
二、歸約

reduce(T iden, BinaryOperator b) 能夠將流中元素反覆結合起來,獲得一個值。返回 Optional。例如使用 reduce 來求全部人員學生的總分的示例代碼以下所示:

Integer all = persons.stream().map(Person::getScore).reduce((integer, integer2) -> integer + integer2).get()
複製代碼
三、收集
  • collect(Collector c) 將流轉換爲其餘形式。它接收一個 Collector 接口的實現,用於給 Stream 中元素作彙總的方法
  • Collector 接口中方法的實現決定了如何對流執行收集操做(如收集到 List、Set、Map)
  • Collectors 實用類提供了不少靜態方法,能夠方便地建立常見收集器實例

收集相關的 Stream API 與其實例代碼以下所示:

  • 1)、toList List 把流中元素收集到 List:
List<Person> emps= list.stream().collect(Collectors.toList());
複製代碼
  • 2)、toSet Set 把流中元素收集 到Set:
Set<Person> emps= list.stream().collect(Collectors.toSet());
複製代碼
  • 3)、toCollection Collection 把流中元素收集到建立的集合:
Collection<Person> emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
複製代碼
  • 4)、counting Long 計算流中元素的個數:
long count = list.stream().collect(Collectors.counting());
複製代碼
  • 5)、summing Int Integer 對流中元素的整數屬性求和:
int total=list.stream().collect(Collectors.summingInt(Person::getAge));
複製代碼
  • 6)、averaging Int Double 計算流中元素 Integer 屬性的平均值:
double avg= list.stream().collect(Collectors.averagingInt(Person::getAge));
複製代碼
  • 7)、summarizingInt IntSummaryStatistics 收集流中 Integer 屬性的統計值。如平均值:
Int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Person::getAge));
複製代碼
  • 8)、joining String 鏈接流中每一個字符串:
String str= list.stream().map(Person::getName).collect(Collectors.joining());
複製代碼
  • 9)、maxBy Optional 根據比較器選擇最大值:
Optional<Person> max= list.stream().collect(Collectors.maxBy(comparingInt(Person::getSalary)));
複製代碼
  • 10)、minBy Optional 根據比較器選擇最小值:
Optional<Person> min = list.stream().collect(Collectors.minBy(comparingInt(Person::getSalary)));
複製代碼
  • 11)、reducing 歸約產生的類型,從一個做爲累加器的初始值開始,利用 BinaryOperator 與流中元素逐個結合,從而歸約成單個值:
int total=list.stream().collect(Collectors.reducing(0, Person::getSalary, Integer::sum));
複製代碼
  • 12)、collectingAndThen 轉換函數返回的類型,包裹另外一個收集器,對其結果轉換函數
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
複製代碼
  • 13)、groupingBy Map<K, List> 根據某屬性值對流分組,屬性爲 K,結果爲 V:
Map<Person.Status, List<Person>> map= list.stream().collect(Collectors.groupingBy(Person::getStatus));
複製代碼
  • 14)、partitioningBy Map<Boolean, List> 根 據true 或 false 進行分區:
Map<Boolean,List<Person>>vd= list.stream().collect(Collectors.partitioningBy(Person::getManage));
複製代碼
四、終止操做練習案例
  • 1)、取出Person對象的全部名字,放到 List 集合中:
List<String> collect2 = persons.stream().map(Person::getName).collect(Collectors.toList());
複製代碼
  • 二、求 Person 對象集合的分數的平均分、總分、最高分,最低分,分數的個數:
IntSummaryStatistics collect = persons.stream().collect(Collectors.summarizingInt(Person::getScore));
System.out.println(collect);
複製代碼
  • 三、根據成績分組,及格的放一組,不及格的放另一組:
Map<Boolean, List<Person>> collect1 = persons.stream().collect(Collectors.partitioningBy(person -> person.getScore() >= 60));
System.out.println(collect1);
複製代碼
  • 四、統計 aa.txt 裏面的單詞數:
public static void main(String[] args) throws IOException {
    InputStream resourceAsStream = Person.class.getClassLoader().getResourceAsStream("aa.txt");
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resourceAsStream));
    bufferedReader.lines().flatMap(x->Stream.of(x.split(" "))).sorted().collect(Collectors.groupingBy(String::toString)).forEach((a,b)-> System.out.println(a+":"+b.size()));
    bufferedReader.close();
}
複製代碼

四、複雜泛型

1)、泛型是什麼?

泛型,即 「參數化類型」。就是將類型由原來的具體的類型參數化,相似於方法中的變量參數,此時類型也定義成參數形式(能夠稱之爲類型形參),而後在使用/調用時傳入具體的類型(類型實參)。

2)、泛型的好處

  • 適用於多種數據類型執行相同的代碼
  • 泛型中的類型在使用時指定,不須要強制類型轉換

3)、泛型類和泛型接口、泛型方法

泛型的本質是爲了參數化類型(在不建立新的類型的狀況下,經過泛型指定的不一樣類型來控制形參具體限制的類型)。而這種參數類型能夠用在類、接口和方法中,分別被稱爲 泛型類、泛型接口、泛型方法

泛型類

引入一個類型變量T(其餘大寫字母均可以,不過經常使用的就是T,E,K,V等等),而且用<>括起來,並放在類名的後面。泛型類是容許有多個類型變量的。常見的示例代碼以下所示:

public class NormalGeneric<K> {
    private K data;

    public NormalGeneric() {
    }

    public NormalGeneric(K data) {
        this.data = data;
    }

    public K getData() {
        return data;
    }

    public void setData(K data) {
        this.data = data;
    }
}
複製代碼
public class NormalGeneric2<T,K> {
    private T data;
    private K result;

    public NormalGeneric2() {
    }

    public NormalGeneric2(T data) {
        this();
        this.data = data;
    }
    
    public NormalGeneric2(T data, K result) {
        this.data = data;
        this.result = result;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public K getResult() {
        return result;
    }

    public void setResult(K result) {
        this.result = result;
    }
}
複製代碼

泛型接口

泛型接口與泛型類的定義基本相同。示例代碼以下所示:

public interface Genertor<T> {
    public T next();
}
複製代碼

可是,實現泛型接口的類,有兩種實現方法

一、未傳入泛型實參

在 new 出類的實例時,須要指定具體類型:

public class ImplGenertor<T> implements Genertor<T> {
    @Override
    public T next() {
        return null;
    }
}
複製代碼
二、傳入泛型實參

在 new 出類的實例時,和普通的類沒區別。

public class ImplGenertor2 implements Genertor<String> {
    @Override
    public String next() {
        return null;
    }
}
複製代碼

泛型方法

泛型方法的 定義在 修飾符與返回值 的中間。示例代碼以下所示:

public <T> T genericMethod(T...a){
    return a[a.length/2];
}
複製代碼

泛型方法,是在調用方法的時候指明泛型的具體類型,泛型方法能夠在任何地方和任何場景中使用,包括普通類和泛型類。

泛型類中定義的普通方法和泛型方法的區別

在普通方法中:

// 雖然在方法中使用了泛型,可是這並非一個泛型方法。
// 這只是類中一個普通的成員方法,只不過他的返回值是在聲明泛型類已經聲明過的泛型。
// 因此在這個方法中才能夠繼續使用 T 這個泛型。
public T getKey(){
    return key;
}
複製代碼

在泛型方法中:

/** * 這纔是一個真正的泛型方法。 * 首先在 public 與返回值之間的 <T> 必不可少,這代表這是一個泛型方法,而且聲明瞭一個泛型 T * 這個 T 能夠出如今這個泛型方法的任意位置,泛型的數量也能夠爲任意多個。 */
public <T,K> K showKeyName(Generic<T> container){
    // ...
}
複製代碼

4)、限定類型變量

public class ClassBorder<T extends Comparable> {
...
}

public class GenericRaw<T extends ArrayList&Comparable> {
...
}
複製代碼

- <T extends Comparable>T 表示應該綁定類型的子類型,Comparable 表示綁定類型,子類型和綁定類型能夠是類也能夠是接口

  • extends 左右都容許有多個,如 T,V extends Comparable&Serializable
  • 注意限定類型中,只容許有一個類,並且若是有類,這個類必須是限定列表的第一個
  • 限定類型變量既能夠用在泛型方法上也能夠用在泛型類上

5)、泛型中的約束和侷限性

  • 一、不能用基本類型實例化類型參數。
  • 二、運行時類型查詢只適用於原始類型。
  • 三、泛型類的靜態上下文中類型變量失效:不能在靜態域或方法中引用類型變量。由於泛型是要在對象建立的時候才知道是什麼類型的,而對象建立的代碼執行前後順序是 static 的部分,而後纔是構造函數等等。因此在對象初始化以前 static 的部分已經執行了,若是你在靜態部分引用泛型,那麼毫無疑問虛擬機根本不知道是什麼東西,由於這個時候類尚未初始化。
  • 四、不能建立參數化類型的數組,可是能夠定義參數化類型的數組。
  • 五、不能實例化類型變量。
  • 六、不能使用 try-catch 捕獲泛型類的實例。

6)、泛型類型的繼承規則

泛型類能夠繼承或者擴展其餘泛型類,好比 List 和 ArrayList:

private static class ExtendPair<T> extends Pair<T>{
...
}
複製代碼

7)、通配符類型

  • ?extends X表示類型的上界,類型參數是 X 的子類
  • ?super X表示類型的下界,類型參數是 X 的超類

?extends X

若是其中提供了 get 和 set 類型參數變量的方法的話,set 方法是不容許被調用的,會出現編譯錯誤,而 get 方法則沒問題。

?extends X 表示類型的上界,類型參數是 X 的子類,那麼能夠確定的說,get 方法返回的必定是個 X(無論是 X 或者 X 的子類)編譯器是能夠肯定知道的。可是 set 方法只知道傳入的是個 X,至於具體是 X 的哪一個子類,是不知道的。

所以,?extends X 主要用於安全地訪問數據,能夠訪問 X 及其子類型,而且不能寫入非 null 的數據

?super X

若是其中提供了 get 和 set 類型參數變量的方法的話,set 方法能夠被調用,且能傳入的參數只能是 X 或者 X 的子類。而 get 方法只會返回一個 Object 類型的值。

? super X 表示類型的下界,類型參數是 X 的超類(包括 X 自己),那麼能夠確定的說,get 方法返回的必定是個 X 的超類,那麼究竟是哪一個超類?不知道,可是能夠確定的說,Object 必定是它的超類,因此 get 方法返回 Object。編譯器是能夠肯定知道的。對於 set 方法來講,編譯器不知道它須要的確切類型,可是 X 和 X 的子類能夠安全的轉型爲 X。

所以,?super X 主要用於安全地寫入數據,能夠寫入 X 及其子類型

無限定的通配符 ?

表示對類型沒有什麼限制,能夠把 ?當作全部類型的父類,如 ArrayList<?>

8)、虛擬機是如何實現泛型的?

泛型思想早在 C++ 語言的模板(Template)中就開始生根發芽,在 Java 語言處於尚未出現泛型的版本時,只能經過 Object 是全部類型的父類和類型強制轉換兩個特色的配合來實現類型泛化

因爲 Java 語言裏面全部的類型都繼承於 java.lang.Object,因此 Object 轉型成任何對象都是有可能的。可是也由於有無限的可能性,就只有程序員和運行期的虛擬機才知道這個 Object 究竟是個什麼類型的對象。在編譯期間,編譯器沒法檢查這個 Object 的強制轉型是否成功,若是僅僅依賴程序員去保障這項操做的正確性,許多 ClassCastException 的風險就會轉嫁到程序運行期之中。

此外,泛型技術在 C#/C++ 和 Java 之中的使用方式看似相同,但實現上卻有着根本性的分歧,C# 裏面的泛型不管在程序源碼中、編譯後的 IL 中(Intermediate Language,中間語言,這時候泛型是一個佔位符),或是運行期的 CLR 中,都是切實存在的,List<int> 與 List<String> 就是兩個不一樣的類型,它們在系統運行期生成,有本身的虛方法表和類型數據,這種實現稱爲類型膨脹,基於這種方法實現的泛型稱爲真實泛型

Java 語言中的泛型則不同,它只在程序源碼中存在,在編譯後的字節碼文件中,就已經替換爲原來的原生類型(Raw Type,也稱爲裸類型)了,而且在相應的地方插入了強制轉型代碼,所以,對於運行期的 Java 語言來講,ArrayList<int> 與 ArrayList<String> 就是同一個類,因此 泛型技術其實是 Java 語言的一顆語法糖,Java 語言中的泛型實現方法稱爲類型擦除,基於這種方法實現的泛型稱爲僞泛型。 將一段 Java 代碼編譯成 Class 文件,而後再用字節碼反編譯工具進行反編譯後,將會發現泛型都不見了,程序又變回了 Java 泛型出現以前的寫法,泛型類型都變回了原生類型。

因爲 Java 泛型的引入,各類場景(虛擬機解析、反射等)下的方法調用均可能對原有的基礎產生影響和新的需求,如在泛型類中如何獲取傳入的參數化類型等。所以,JCP 組織對虛擬機規範作出了相應的修改,引入了諸如 Signature、LocalVariableTypeTable 等新的屬性用於解決伴隨泛型而來的參數類型的識別問題,Signature 是其中最重要的一項屬性,它的做用就是存儲一個方法在字節碼層面的特徵簽名,這個屬性中保存的參數類型並非原生類型,而是包括了參數化類型的信息。修改後的虛擬機規範要求全部能識別 49.0 以上版本的 Class 文件的虛擬機都要能正確地識別 Signature 參數。

最後,從 Signature 屬性的出現咱們還能夠得出結論,擦除法所謂的擦除,僅僅是對方法的 Code 屬性中的字節碼進行擦除,實際上元數據中仍是保留了泛型信息,這也是咱們能經過反射手段取得參數化類型的根本依據

2、初識 ByteX

ByteX 使用了純 Java 來編寫源碼,它是一個基於 Gradle transform api 和 ASM 的字節碼插樁平臺。

調試:gradle clean :example:assembleRelease -Dorg.gradle.debug=true --no-daemon

一、優點

  • 1)、自動集成到其它宿主和插件一塊兒整合爲一個單獨的 MainTransformFlow,結合 class 文件多線程併發處理,避免了打包的額外時間呈線性增加
  • 2)、插件、宿主之間徹底解耦,便於協同開發
  • 3)、common module 提供通用的代碼複用,每一個插件只需專一自身的字節碼插樁邏輯

二、MainTransformFlow 基本流程

在 MainTransformFlow implements MainProcessHandler 常規處理過程,會遍歷兩次工程構建中的全部 class。

  • 1)、第一次,遍歷 traverse 與 traverseAndroidJar 過程,以造成完整的類圖。
  • 2)、第二次,執行 transform:再遍歷一次工程中全部的構建產物,並對 class 文件作處理後輸出。

三、如何自定義獨立的 TransformFlow?

重寫 IPlugin 的 provideTransformFlow 便可。

四、類圖對象

context.getClassGraph() 獲取類圖對象,兩個 TransformFlow 的類圖是隔離的。

五、MainProcessHandler

  • 經過複寫 process 方法,註冊本身的 FlieProcessor 來處理。
  • FileProcessor 採用了責任鏈模式,每一個 class 文件都會流經一系列的 FileProcessor 來處理。

六、IPlugin.hookTransformName()

使用 反射 Hook 方式 將 Transform 註冊到 proguard 以後

3、ByteX 插件平臺構建流程探祕

添加 apply plugin: 'bytex' 以後,bytex 能夠在 Gradle 的構建流程中起做用了。這裏的插件 id 爲 bytex,咱們找到 bytex.properties 文件,查看裏面映射的實現類,以下所示:

implementation-class=com.ss.android.ugc.bytex.base.ByteXPlugin
複製代碼

能夠看到,bytex 的實現類爲 ByteXPlugin,其源碼以下所示:

public class ByteXPlugin implements Plugin<Project> {
    @Override
    public void apply(@NotNull Project project) {
        // 1
        AppExtension android = project.getExtensions().getByType(AppExtension.class);
        // 2
        ByteXExtension extension = project.getExtensions().create("ByteX", ByteXExtension.class);
        // 3
        android.registerTransform(new ByteXTransform(new Context(project, android, extension)));
    }
}
複製代碼

首先,註釋1處,獲取 Android 爲 App 提供的擴展屬性 AppExtension 實例。而後,在註釋2處,獲取 ByteX 自身建立的擴展屬性 ByteXExtension 實例。最後,在註釋3處,註冊 ByteXTransform 實例。ByteXTransform 繼承了抽象類 CommonTransform,其實現了關鍵的 transform 方法,其實現源碼以下所示:

@Override
public final void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
    super.transform(transformInvocation);
    // 一、若是不是增量模式,則清楚輸出目錄的文件。
    if (!transformInvocation.isIncremental()) {
        transformInvocation.getOutputProvider().deleteAll();
    }
    // 二、獲取 transformContext 實例。
    TransformContext transformContext = getTransformContext(transformInvocation);
    // 三、初始化 HtmlReporter(生成 ByteX 構建產生日誌的 HTML 文件)
    init(transformContext);
    // 四、過濾掉沒有打開插件開關的 plugin。
    List<IPlugin> plugins = getPlugins().stream().filter(p -> p.enable(transformContext)).collect(Collectors.toList());
    Timer timer = new Timer();
    // 五、建立一個 transformEngine 實例。
    TransformEngine transformEngine = new TransformEngine(transformContext);
    try {
        if (!plugins.isEmpty()) {
            // 六、使用 PriorityQueue 對每個 TransformFlow 進行優先級排序(在這裏添加的是與之對應的實現類 MainTransformFlow)。
            Queue<TransformFlow> flowSet = new PriorityQueue<>((o1, o2) -> o2.getPriority() - o1.getPriority());
            MainTransformFlow commonFlow = new MainTransformFlow(transformEngine);
              flowSet.add(commonFlow);
            for (int i = 0; i < plugins.size(); i++) {
                // 七、給每個 Plugin 註冊 MainTransformFlow,其實質是將每個 Plugin 的 MainProcessHandler 添加到 MainTransformFlow 中的 handlers 列表中。
                IPlugin plugin = plugins.get(i);
                TransformFlow flow = plugin.registerTransformFlow(commonFlow, transformContext);
                if (!flowSet.contains(flow)) {
                    flowSet.add(flow);
                }
            }
            while (!flowSet.isEmpty()) {
                TransformFlow flow = flowSet.poll();
                if (flow != null) {
                    if (flowSet.size() == 0) {
                        flow.asTail();
                    }
                    // 八、按指定優先級執行每個 TransformFlow 的 run 方法,默認只有一個 MainTransformFlow 實例。
                    flow.run();
                    // 九、獲取流中的 graph 類圖對象並清除。
                    Graph graph = flow.getClassGraph();
                    if (graph != null) {
                        //clear the class diagram.we won’t use it anymore
                        graph.clear();
                    }
                }
            }
        } else {
            transformEngine.skip();
        }
        // 10
        afterTransform(transformInvocation);
    } catch (Throwable throwable) {
        LevelLog.sDefaultLogger.e(throwable.getClass().getName(), throwable);
        throw throwable;
    } finally {
        for (IPlugin plugin : plugins) {
            try {
                plugin.afterExecute();
            } catch (Throwable throwable) {
                LevelLog.sDefaultLogger.e("do afterExecute", throwable);
            }
        }
        transformContext.release();
        release();
        timer.record("Total cost time = [%s ms]");
        if (BooleanProperty.ENABLE_HTML_LOG.value()) {
            HtmlReporter.getInstance().createHtmlReporter(getName());
            HtmlReporter.getInstance().reset();
        }
    }
}
複製代碼

在註釋7處,調用了 plugin.registerTransformFlow 方法,其源碼以下所示:

@Nonnull
@Override
public final TransformFlow registerTransformFlow(@Nonnull MainTransformFlow mainFlow, @Nonnull TransformContext transformContext) {
    if (transformFlow == null) {
        transformFlow = provideTransformFlow(mainFlow, transformContext);
        if (transformFlow == null) {
            throw new RuntimeException("TransformFlow can not be null.");
        }
    }
    return transformFlow;
}
複製代碼

這裏繼續調用了 provideTransformFlow 方法,其源碼以下所示:

/** * create a new transformFlow or just return mainFlow and append a handler. * It will be called by {@link IPlugin#registerTransformFlow(MainTransformFlow, TransformContext)} when * handle start. * * @param mainFlow main TransformFlow * @param transformContext handle context * @return return a new TransformFlow object if you want make a new flow for current plugin */
protected TransformFlow provideTransformFlow(@Nonnull MainTransformFlow mainFlow, @Nonnull TransformContext transformContext) {
    return mainFlow.appendHandler(this);
}
複製代碼

能夠看到,經過調用 mainFlow.appendHandler(this) 方法將每個 Plugin 的 MainProcessHandler 添加到 MainTransformFlow 中的 handlers 列表之中。

在註釋8處,按指定優先級執行了每個 TransformFlow 的 run 方法,默認只有一個 MainTransformFlow 實例。咱們看到了 MianTransformFlow 的 run 方法:

@Override
public void run() throws IOException, InterruptedException {
    try {
        // 1
        beginRun();
        // 2
        runTransform();
    } finally {
        // 3
        endRun();
    }
}
複製代碼

首先,在註釋1出,調用了 beginRun 方法,其實現以下:

// AbsTransformFlow
protected void beginRun() {
    transformEngine.beginRun();
}

// TransformEngine
public void beginRun(){
    context.markRunningState(false);
}

// TransformContext
private final AtomicBoolean running = new AtomicBoolean(false);

void markRunningState(boolean running) {
    this.running.set(running);
}
複製代碼

最後,在 TransformContext 實例中使用了一個 AtomicBoolean 實例標記 MainTransformFlow 是否正在運行中。

而後,在註釋2處執行了 runTransform 方法,這裏就是真正執行 transform 的地方,其源碼以下所示:

private void runTransform() throws IOException, InterruptedException {
    if (handlers.isEmpty()) return;
    Timer timer = new Timer();
    timer.startRecord("PRE_PROCESS");
    timer.startRecord("INIT");
    // 一、初始化 handlers 列表中的每個 handler。
    for (MainProcessHandler handler : handlers) {
        handler.init(transformEngine);
    }
    timer.stopRecord("INIT", "Process init cost time = [%s ms]");
    // 若是不是 跳過 traverse 僅僅只執行 Transform 方法時,才執行 traverse 過程。
    if (!isOnePassEnough()) {
        if (!handlers.isEmpty() && context.isIncremental()) {
            timer.startRecord("TRAVERSE_INCREMENTAL");
            // 二、若是是 增量模式,則執行 traverseArtifactOnly(僅僅增量遍歷產物)調用每個 plugin 的對應的 MainProcessHandler 的 traverseIncremental 方法。這裏最終會調用 ClassFileAnalyzer.handle 方法進行遍歷分發操做。
            traverseArtifactOnly(getProcessors(Process.TRAVERSE_INCREMENTAL, new ClassFileAnalyzer(context, Process.TRAVERSE_INCREMENTAL, null, handlers)));
            timer.stopRecord("TRAVERSE_INCREMENTAL", "Process project all .class files cost time = [%s ms]");
        }
        handlers.forEach(plugin -> plugin.beforeTraverse(transformEngine));
        timer.startRecord("LOADCACHE");
        // 三、建立一個 CachedGraphBuilder 對象:可以緩存 類圖 的 類圖構建者對象。
        GraphBuilder graphBuilder = new CachedGraphBuilder(context.getGraphCache(), context.isIncremental(), context.shouldSaveCache());
        if (context.isIncremental() && !graphBuilder.isCacheValid()) {
            // 四、若是是增量更新 && graphBuilder 的緩存失效則直接請求非增量運行。
            context.requestNotIncremental();
        }
        timer.stopRecord("LOADCACHE", "Process loading cache cost time = [%s ms]");
        // 五、內部會調用 running.set(true) 來標記正在運行的狀態。
        running();
        if (!handlers.isEmpty()) {
            timer.startRecord("PROJECT_CLASS");
            // 六、執行 traverseArtifactOnly(遍歷產物)調用每個 plugin 的對應的 MainProcessHandler 的 traverse 方法,這裏最終會調用 ClassFileAnalyzer.handle 方法進行遍歷分發操做。
            traverseArtifactOnly(getProcessors(Process.TRAVERSE, new ClassFileAnalyzer(context, Process.TRAVERSE, graphBuilder, handlers)));
            timer.stopRecord("PROJECT_CLASS", "Process project all .class files cost time = [%s ms]");
        }
        if (!handlers.isEmpty()) {
            timer.startRecord("ANDROID");
            // 七、僅僅遍歷 Android.jar
            traverseAndroidJarOnly(getProcessors(Process.TRAVERSE_ANDROID, new ClassFileAnalyzer(context, Process.TRAVERSE_ANDROID, graphBuilder, handlers)));
            timer.stopRecord("ANDROID", "Process android jar cost time = [%s ms]");
        }
        timer.startRecord("SAVECACHE");
        // 八、構建 mClassGraph 類圖實例。
        mClassGraph = graphBuilder.build();
        timer.stopRecord("SAVECACHE", "Process saving cache cost time = [%s ms]");
    }
    timer.stopRecord("PRE_PROCESS", "Collect info cost time = [%s ms]");
    if (!handlers.isEmpty()) {
        timer.startRecord("PROCESS");
        // 九、遍歷執行每個 plugin 的 transform 方法。
        transform(getProcessors(Process.TRANSFORM, new ClassFileTransformer(handlers, needPreVerify(), needVerify())));
        timer.stopRecord("PROCESS", "Transform cost time = [%s ms]");
    }
}
複製代碼

首先,在註釋1處,遍歷調用了每個 MainProcessHandler 的 init 方法,它是用於 transform 開始前的初始化實現方法。

MainProcessHandler 接口的 init 方法是一個 default 方法,裏面直接調用了每個 pluign 實現的 init 方法(若是 plugin 沒有實現,則僅僅調用 CommonPlugin 實現的 init 方法:這裏一般是用於把不須要處理的文件添加到 mWhiteList 列表),這裏能夠作一些 plugin 的準備工做。

一、僅僅遍歷產物

traverseArtifactOnly(getProcessors(Process.TRAVERSE, new ClassFileAnalyzer(context, Process.TRAVERSE, graphBuilder, handlers)));
複製代碼

getProcessors 方法的源碼以下所示:

private FileProcessor[] getProcessors(Process process, FileHandler fileHandler) {
    List<FileProcessor> processors = handlers.stream()
            .flatMap((Function<MainProcessHandler, Stream<FileProcessor>>) handler -> handler.process(process).stream())
            .collect(Collectors.toList());
    switch (process) {
        case TRAVERSE_INCREMENTAL:
            processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED));
            processors.add(new IncrementalFileProcessor(handlers, ClassFileProcessor.newInstance(fileHandler)));
            break;
        case TRAVERSE:
        case TRAVERSE_ANDROID:
        case TRANSFORM:
            processors.add(ClassFileProcessor.newInstance(fileHandler));
            processors.add(0, new FilterFileProcessor(fileData -> fileData.getStatus() != Status.NOTCHANGED && fileData.getStatus() != Status.REMOVED))
            break;
        default:
            throw new RuntimeException("Unknow Process:" + process);
    }
    return processors.toArray(new FileProcessor[0]);
}
複製代碼

這裏的 processor 的添加由 增量 進行界定,具體的處理標準以下:

  • TRAVERSE_INCREMENTAL
    • FilterFileProcessor按照不一樣的過程過濾掉不須要的 FileData
    • IncrementalFileProcessor用於進行 增量文件的處理
  • TRAVERSE/TRAVERSE_ANDROID/TRANSFORM:
    • FilterFileProcessor按照不一樣的過程過濾掉不須要的 FileData
    • ClassFileProcessor用於處理 .class 文件

二、僅僅遍歷 Android Jar 包

traverseAndroidJarOnly(getProcessors(Process.TRAVERSE_ANDROID, new ClassFileAnalyzer(context, Process.TRAVERSE_ANDROID, graphBuilder, handlers)));
複製代碼

三、構建 mClassGraph 類圖對象

mClassGraph = graphBuilder.build();
複製代碼

四、執行 Transform

transform(getProcessors(Process.TRANSFORM, new ClassFileTransformer(handlers, needPreVerify(), needVerify())));
複製代碼

transform 的源碼以下所示:

// AbsTransformFlow 類中
protected AbsTransformFlow transform(FileProcessor... processors) throws IOException, InterruptedException {
        beforeTransform(transformEngine);
        transformEngine.transform(isLast, processors);
        afterTransform(transformEngine);
        return this;
    }
複製代碼

1)、beforeTransform(transformEngine)

// MainTransformFlow
@Override
protected AbsTransformFlow beforeTransform(TransformEngine transformEngine) {
    // 1
    handlers.forEach(plugin -> plugin.beforeTransform(transformEngine));
    return this;
}
複製代碼

註釋1處,遍歷執行 每個 plugin 的 beforeTransform 方法作一些自身 transform 前的準備工做。

2)、transformEngine.transform(isLast, processors)

// TranformEngine
public void transform(boolean isLast, FileProcessor... processors) {
    Schedulers.FORKJOINPOOL().invoke(new PerformTransformTask(context.allFiles(), getProcessorList(processors), isLast, context));
}
複製代碼

Shedulers.FORKJOINPOOL() 方法的源碼以下所示:

public class Schedulers {
    private static final int cpuCount = Runtime.getRuntime().availableProcessors();
    private final static ExecutorService IO = new ThreadPoolExecutor(0, cpuCount * 3,
            30L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>());

    // 1
    private static final ExecutorService COMPUTATION = Executors.newWorkStealingPool(cpuCount);

    public static Worker IO() {
        return new Worker(IO);
    }

    public static Worker COMPUTATION() {
        return new Worker(COMPUTATION);
    }

    public static ForkJoinPool FORKJOINPOOL() {
        return (ForkJoinPool) COMPUTATION;
    }
}
複製代碼

能夠看到,最終是執行 Executors.newWorkStealingPool(cpuCount) 方法生成了一個 ForkJoinPool 實例。

ForkJoinPool 與 ThreadPoolExecutor 是屬於平級關係,ForkJoinPool 線程池是爲了實現「分治法」這一思想而建立的,經過把大任務拆分紅小任務,而後再把小任務的結果彙總起來就是最終的結果,和 MapReduce 的思想很相似。除了「分治法」以外,ForkJoinPool 還使用了工做竊取算法,即全部線程均嘗試找到並執行已提交的任務,或是經過其餘任務建立的子任務。有了它咱們就能夠儘可能避免一個線程執行完本身的任務後「無所事事」的狀況。

而後這裏會回調 PerformTransformTask 實例的 compute 方法,源碼以下所示:

@Override
protected void compute() {
    if (outputFile) {
        // 一、若是是最後一個 TransformFlow,則遞歸調用全部的 FileTransformTask。
        List<FileTransformTask> tasks = source.map(cache -> new FileTransformTask(context, cache, processors)).collect(Collectors.toList());
        // 二、對於Fork/Join模式,假如Pool裏面線程數量是固定的,那麼調用子任務的fork方法至關於A先分工給B,而後A當監工不幹活,B去完成A交代的任務。因此上面的模式至關於浪費了一個線程。那麼若是使用invokeAll至關於A分工給B後,A和B都去完成工做。這樣能夠更好的利用線程池,縮短執行的時間。
        invokeAll(tasks);
    } else {
        // 三、、遞歸調用 FileTransformTask
        PerformTraverseTask traverseTask = new PerformTraverseTask(source, processors);
        invokeAll(traverseTask);
    }
}
複製代碼

在註釋1處,若是是最後一個 TransformFlow,則調用全部的 FileTransformTask。註釋2處,對於 Fork/Join 模式,假如 Pool 裏面線程數量是固定的,那麼調用子任務的 fork 方法至關於 A 先分工給 B,而後 A 當監工不幹活,B 去完成 A 交代的任務。因此上面的模式至關於浪費了一個線程。那麼若是使用 invokeAll 至關於 A 分工給 B 後,A 和 B 都去完成工做。這樣能夠更好的利用線程池,縮短執行的時間。註釋3處,執行了 ForkJoinTask 的 invokeAll 方法,這裏便會回調 compute 方法,源碼以下所示:

@Override
protected void compute() {
    List<FileTraverseTask> tasks = source.map(cache -> new FileTraverseTask(cache, processors)).collect(Collectors.toList());
    // 1
    invokeAll(tasks);
}
複製代碼

註釋1處,繼續回調全部的 FileTraverseTask 實例的 compute 方法,源碼以下所示:

@Override
protected void compute() {
    List<TraverseTask> tasks = fileCache.stream().map(file -> new TraverseTask(fileCache, file, processors))
            .toList().blockingGet();
    // 1
    invokeAll(tasks);
}
複製代碼

註釋1處,繼續回調全部的 TraverseTask 實例的 compute 方法,源碼以下所示:

@Override
protected void compute() {
    try {
        Input input = new Input(fileCache.getContent(), file);
        ProcessorChain chain = new ProcessorChain(processors, input, 0);
        // 一、調用 ProcessorChain 的 proceed 方法。
        chain.proceed(input);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
複製代碼

註釋1處,調用了 ProcessorChain 的 proceed 方法。源碼以下所示:

@Override
public Output proceed(Input input) throws IOException {
    if (index >= processors.size()) throw new AssertionError();
    // 1
    FileProcessor next = processors.get(index);
    
    return next.process(new ProcessorChain(processors, input, index + 1));
}
複製代碼

註釋1處,會從 processors 處理器列表中獲取第一個處理器—FilterFileProcessor,並調用它的 process 方法,源碼以下所示:

@Override
public Output process(Chain chain) throws IOException {
    Input input = chain.input();
    if (predicate.test(input.getFileData())) {
        // 1
        return chain.proceed(input);
    } else {
        return new Output(input.getFileData());
    }
}
複製代碼

註釋1處,若是有 FileData 的話,則繼續調用 chain 的 proceed 方法,內部會繼續調用 ClassFileProcessor 的 process 方法,源碼以下:

@Override
public Output process(Chain chain) throws IOException {
    Input input = chain.input();
    FileData fileData = input.getFileData();
    if (fileData.getRelativePath().endsWith(".class")) {
        // 一、若是 fileData 是 .class 文件,則調用 ClassFileTransformer 的 handle 方法進行處理。
        handler.handle(fileData);
    }
    // 二、
    return chain.proceed(input);
}
複製代碼

註釋1處,若是 fileData 是 .class 文件,則調用 ClassFileTransformer 的 handle 方法進行處理。其源碼以下所示:

@Override
public void handle(FileData fileData) {
    try {
        byte[] raw = fileData.getBytes();
        String relativePath = fileData.getRelativePath();
        int cwFlags = 0;  //compute nothing
        int crFlags = 0;
        for (MainProcessHandler handler : handlers) {
            // 一、設置 ClassWrite 的 flag 的默認值爲 ClassWriter.COMPUTE_MAXS。
            cwFlags |= handler.flagForClassWriter();
            if ((handler.flagForClassReader(Process.TRANSFORM) & ClassReader.EXPAND_FRAMES) == ClassReader.EXPAND_FRAMES) {
                crFlags |= ClassReader.EXPAND_FRAMES;
            }
        }
        ClassReader cr = new ClassReader(raw);
        ClassWriter cw = new ClassWriter(cwFlags);
        ClassVisitorChain chain = getClassVisitorChain(relativePath);
        if (needPreVerify) {
            // 二、若是須要預校驗,則將責任鏈表頭尾部設置爲 AsmVerifyClassVisitor 實例。
            chain.connect(new AsmVerifyClassVisitor());
        }
        if (handlers != null && !handlers.isEmpty()) {
            for (MainProcessHandler handler : handlers) {
                // 三、遍歷執行全部 plugin 的 transform。其內部會使用 chain.connect(new ReferCheckClassVisitor(context)) 的方式 
                if (!handler.transform(relativePath, chain)) {
                    fileData.delete();
                    return;
                }
            }
        }
        // 四、兼容 ClassNode 處理的模式
        ClassNode cn = new SafeClassNode();
        chain.append(cn);
        chain.accept(cr, crFlags);
        for (MainProcessHandler handler : handlers) {
            if (!handler.transform(relativePath, cn)) {
                fileData.delete();
                return;
            }
        }
        cn.accept(cw);
        if (!GlobalWhiteListManager.INSTANCE.shouldIgnore(fileData.getRelativePath())) {
            raw = cw.toByteArray();
            if (needVerify) {
                ClassNode verifyNode = new ClassNode();
                new ClassReader(raw).accept(verifyNode, crFlags);
                AsmVerifier.verify(verifyNode);
            }
            // 五、若是不是白名單裏的文件,則將 ClassWriter 中的數據放入 fileData 之中。
            fileData.setBytes(raw);
        }
    } catch (ByteXException e) {
        throw e;
    } catch (Exception e) {
        LevelLog.sDefaultLogger.e(String.format("Failed to handle class %s", fileData.getRelativePath()), e);
        if (!GlobalWhiteListManager.INSTANCE.shouldIgnore(fileData.getRelativePath())) {
            throw e;
        }
    }
}
複製代碼

在 ClassFileProcessor 的 process 方法的註釋2處,又會繼續調用 ProcessorChain 的 proceed 方法,這裏會回調 BackupFileProcessor 實例的 process 方法,源碼以下所示:

@Override
public Output process(Chain chain) throws IOException {
    Input input = chain.input();
    // 僅僅是返回處理過的輸出文件
    return new Output(input.getFileData());
}
複製代碼

按照這樣的模式,PerformTraverseTask 實例的全部 task 都被遍歷執行了。

最後,便會調用 MainTransformFlow 實例的 afterTransform 方法,源碼以下:

@Override
protected AbsTransformFlow afterTransform(TransformEngine transformEngine) {
    handlers.forEach(plugin -> plugin.afterTransform(transformEngine));
    return this;
}
複製代碼

這裏遍歷執行了全部 plugin 的 afterTransform 方法。

而後,咱們再回到 CommonTransform 的 transform 方法,在執行完 MainTransformFlow 的 run 方法後,便會調用註釋9處的代碼來獲取流中的 graph 類圖對象並清除。最後,執行註釋10處的 afterTransform 方法用來作 transform 以後的收尾工做。

4、總結

在本文中,咱們一塊兒對 ByteX 插件平臺的構建流程進行了探祕。從 ByteX 的源碼實現中,咱們能夠看出做者對 函數式編程、Java 1.8 Lambda 表達式、Java 1.8 Stream API、複雜泛型 等技術的靈活運用,因此,幾乎全部看似很 🐂 的輪子,其實質都是依賴於對基礎技術的深度掌握。那麼,如何才能達到深度掌握基礎技術的程度呢?— 惟有不斷地練習與有規律的複習

參考連接:


很感謝您閱讀這篇文章,但願您能將它分享給您的朋友或技術羣,這對我意義重大。

但願咱們能成爲朋友,在 Github掘金上一塊兒分享知識。

相關文章
相關標籤/搜索