面向對象編程是對數據進行抽象,而函數式編程是對行爲進行抽象。現實世界中,數據和行爲並存,而程序也是如此。java
Lambda 是一個匿名函數,即沒有函數名的函數,它簡化了匿名委託的使用,讓代碼更加簡潔。python
//匿名內部類
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())); 複製代碼
Lambda 表達式在 Java 語言中引入了一個新的語法元素和操做符。這個操做符爲 「->」 ,該操做符被稱爲 Lambda 操做符或剪頭操做符。android
它將 Lambda 分爲兩個部分:git
() -> System.out.println("Hello Lambda!");
(x) -> System.out.println(x)
x -> System.out.println(x)
Comparator<Integer> com = (x, y) -> {
System.out.println("函數式接口"); return Integer.compare(x, y); }; 複製代碼
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
(Integer x, Integer y) -> Integer.compare(x, y);
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); } 複製代碼
接口 | 參數 | 返回類型 | 示例 |
---|---|---|---|
Predicate | T | boolean | 這道題對了嗎? |
Consumer | T | void | 輸出一個值 |
Function<T,R> | T | R | 得到 Person對象的名字 |
Supplier | None | T | 工廠方法 |
UnaryOperator | T | T | 邏輯非 (!) |
BinaryOperator | (T, T) | T | 求兩個數的乘積 (*) |
當要傳遞給 Lambda 體的操做,已經有實現的方法了,可使用方法引用。方法引用使用 「操做符 「 ::」 將方法名和對象或類的名字分隔開來」。它主要有以下 「三種」 使用狀況 :github
在咱們使用 Lambda 表達式的時候,」->」 右邊部分是要執行的代碼,即要完成的功能,能夠把這部分稱做 Lambda 體。有時候,「當咱們想要實現一個函數式接口的那個抽象方法,可是已經有類實現了咱們想要的功能,這個時候咱們就能夠用方法引用來直接使用現有類的功能去實現」。示例代碼以下所示:web
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::methodName
。編程
格式: ClassName :: new
與函數式接口相結合,自動與函數式接口中方法兼容。 能夠把構造器引用賦值給定義的方法,可是構造器參數列表要與接口中抽象方法的參數列表一致。示例以下所示:json
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; } 複製代碼
格式: type[] :: new,示例以下所示:
public static void main(String[] args) {
Function<Integer,Person[]> f = x->new Person[x]; Function<Integer,Person[]> f2 = Person[]::new; } 複製代碼
「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); } 複製代碼
建立流主要有四種方式,其示例代碼以下所示:
@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); } 複製代碼
這裏,咱們給出一些常見的使用示例,以下所示:
List<Integer> collect = Arrays.stream(ary).skip(2).limit(3).collect(Collectors.toList());
複製代碼
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()); 複製代碼
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);
複製代碼
終端操做會從流的流水線生成結果。其結果能夠是任何不是流的值,例如: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()
複製代碼
收集相關的 Stream API 與其實例代碼以下所示:
List<Person> emps= list.stream().collect(Collectors.toList());
複製代碼
Set<Person> emps= list.stream().collect(Collectors.toSet());
複製代碼
Collection<Person> emps=list.stream().collect(Collectors.toCollection(ArrayList::new));
複製代碼
long count = list.stream().collect(Collectors.counting());
複製代碼
int total=list.stream().collect(Collectors.summingInt(Person::getAge));
複製代碼
double avg= list.stream().collect(Collectors.averagingInt(Person::getAge));
複製代碼
Int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Person::getAge));
複製代碼
String str= list.stream().map(Person::getName).collect(Collectors.joining());
複製代碼
Optional<Person> max= list.stream().collect(Collectors.maxBy(comparingInt(Person::getSalary)));
複製代碼
Optional<Person> min = list.stream().collect(Collectors.minBy(comparingInt(Person::getSalary)));
複製代碼
int total=list.stream().collect(Collectors.reducing(0, Person::getSalary, Integer::sum));
複製代碼
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
複製代碼
Map<Person.Status, List<Person>> map= list.stream().collect(Collectors.groupingBy(Person::getStatus));
複製代碼
Map<Boolean,List<Person>>vd= list.stream().collect(Collectors.partitioningBy(Person::getManage));
複製代碼
List<String> collect2 = persons.stream().map(Person::getName).collect(Collectors.toList());
複製代碼
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); 複製代碼
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(); } 複製代碼
泛型,即 「「參數化類型」」。就是將類型由原來的具體的類型參數化,相似於方法中的變量參數,此時類型也定義成參數形式(能夠稱之爲類型形參),而後在使用/調用時傳入具體的類型(類型實參)。
泛型的本質是爲了參數化類型(在不建立新的類型的狀況下,經過泛型指定的不一樣類型來控制形參具體限制的類型)。而這種參數類型能夠用在類、接口和方法中,分別被稱爲 「泛型類、泛型接口、泛型方法」。
引入一個類型變量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){ // ... } 複製代碼
public class ClassBorder<T extends Comparable> {
... } public class GenericRaw<T extends ArrayList&Comparable> { ... } 複製代碼
-<T extends Comparable>
:「T 表示應該綁定類型的子類型,Comparable 表示綁定類型,子類型和綁定類型能夠是類也能夠是接口」。
泛型類能夠繼承或者擴展其餘泛型類,好比 List 和 ArrayList:
private static class ExtendPair<T> extends Pair<T>{
... } 複製代碼
?extends X
:
「表示類型的上界,類型參數是 X 的子類」。
?super X
:
「表示類型的下界,類型參數是 X 的超類」。
若是其中提供了 get 和 set 類型參數變量的方法的話,set 方法是不容許被調用的,會出現編譯錯誤,而 get 方法則沒問題。
?extends X 表示類型的上界,類型參數是 X 的子類,那麼能夠確定的說,get 方法返回的必定是個 X(無論是 X 或者 X 的子類)編譯器是能夠肯定知道的。可是 set 方法只知道傳入的是個 X,至於具體是 X 的哪一個子類,是不知道的。
所以,「?extends X 主要用於安全地訪問數據,能夠訪問 X 及其子類型,而且不能寫入非 null 的數據」。
若是其中提供了 get 和 set 類型參數變量的方法的話,set 方法能夠被調用,且能傳入的參數只能是 X 或者 X 的子類。而 get 方法只會返回一個 Object 類型的值。
? super X 表示類型的下界,類型參數是 X 的超類(包括 X 自己),那麼能夠確定的說,get 方法返回的必定是個 X 的超類,那麼究竟是哪一個超類?不知道,可是能夠確定的說,Object 必定是它的超類,因此 get 方法返回 Object。編譯器是能夠肯定知道的。對於 set 方法來講,編譯器不知道它須要的確切類型,可是 X 和 X 的子類能夠安全的轉型爲 X。
所以,「?super X 主要用於安全地寫入數據,能夠寫入 X 及其子類型」。
「表示對類型沒有什麼限制,能夠把 ?當作全部類型的父類,如 ArrayList<?>」。
泛型思想早在 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 屬性中的字節碼進行擦除,實際上元數據中仍是保留了泛型信息,這也是咱們能經過反射手段取得參數化類型的根本依據」。
ByteX 使用了純 Java 來編寫源碼,它是一個基於 Gradle transform api 和 ASM 的字節碼插樁平臺。
❝調試:gradle clean :example:assembleRelease -Dorg.gradle.debug=true --no-daemon
❞
在 MainTransformFlow implements MainProcessHandler 常規處理過程,會遍歷兩次工程構建中的全部 class。
重寫 IPlugin 的 provideTransformFlow 便可。
context.getClassGraph() 獲取類圖對象,兩個 TransformFlow 的類圖是隔離的。
「使用 反射 Hook 方式 將 Transform 註冊到 proguard 以後」。
添加 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 文件」。
traverseAndroidJarOnly(getProcessors(Process.TRAVERSE_ANDROID, new ClassFileAnalyzer(context, Process.TRAVERSE_ANDROID, graphBuilder, handlers)));
複製代碼
mClassGraph = graphBuilder.build();
複製代碼
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; } 複製代碼
// MainTransformFlow
@Override protected AbsTransformFlow beforeTransform(TransformEngine transformEngine) { // 1 handlers.forEach(plugin -> plugin.beforeTransform(transformEngine)); return this; } 複製代碼
註釋1處,遍歷執行 每個 plugin 的 beforeTransform 方法作一些自身 transform 前的準備工做。
// 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 以後的收尾工做。
在本文中,咱們一塊兒對 ByteX 插件平臺的構建流程進行了探祕。從 ByteX 的源碼實現中,咱們能夠看出做者對 函數式編程、Java 1.8 Lambda 表達式、Java 1.8 Stream API、複雜泛型 等技術的靈活運用,因此,「幾乎全部看似很 🐂 的輪子,其實質都是依賴於對基礎技術的深度掌握」。那麼,「如何才能達到深度掌握基礎技術的程度呢?— 惟有不斷地練習與有規律的複習」。
個人公衆號 JsonChao
開通啦,歡迎關注~
一、ByteX
三、《深刻理解 JVM》
四、《Java 編程思想》
❝歡迎關注個人微信:
❞bcce5360
❝「因爲微信羣已超過 200 人,麻煩你們想進微信羣的朋友們,加我微信拉你進羣。」
❞
❝2千人QQ羣,「Awesome-Android學習交流羣,QQ羣號:959936182」, 歡迎你們加入~
❞