面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,而程序也是如此。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」, 欢迎你们加入~
❞