一篇文章教你如何在Android编译期插桩,让程序学会自己写代码

==

近些年,编译期插桩技术在Android圈越来越普遍。无论是可以生成JAVA源码的ButterKnief、Dagger,还是操作字节码的VirtualAPK,甚至是新兴的语言Kotlin都用到了编译期插桩技术。学习这门技术对我们理解这些框架的原理十分有帮助。另外,我们通过这种技术可以抽离出复杂、重复的代码,降低程序耦合性,提高代码的可复用性,提高开发效率。因此,了解编译期插桩技术十分必要。在介绍这项技术之前,我们先来了解一下Android代码的编译过程以及插桩位置。话不多说,直接上图。

一篇文章教你如何在Android编译期插桩,让程序学会自己写代码_第1张图片

APT

===

APT(Annotation Processing Tool)是一种编译期注解处理器。它通过定义注解和处理器来实现编译期生成代码的功能,并且将生成的代码和源代码一起编译成.class文件。

代表框架:ButterKnife、Dagger、ARouter、EventBus3、DataBinding、AndroidAnnotation等。

在介绍如何应用APT技术之前,我们先来了解一些相关的知识。

一、Element

=========

1.简介


Element是一种在编译期描述.java文件静态结构的一种类型,它可能表示一个package、一个class、一个method或者一个field。Element的比较应该使用equals,因为编译期间同一个Element可能会用两个对象表示。JDK提供了以下5种Element

一篇文章教你如何在Android编译期插桩,让程序学会自己写代码_第2张图片

2.Element的存储结构


编译器采用类似Html的Dom树来存储Element。我们用下面的Test.java来具体说明。

//PackageElement

package me.zhangkuo.compile;

//TypeElement

public class Test {

//VariableElement

private String name;

//ExecutableElement

private Test(){

}

//ExecutableElement

public void setName(/* TypeParameterElement */ String name) {

this.name = name;

}

}

Test.java用Element树结构描述如下:

一篇文章教你如何在Android编译期插桩,让程序学会自己写代码_第3张图片

我们可以看到 setName(String name)ExecutableElement中并没有子节点TypeParameterElement。这是因为TypeParameterElement没有被纳入到Element树中。不过我们可以通过ExecutableElementgetTypeParameters()方法来获取。

此外,再给大家介绍两个Element中十分有用的方法。

public interface Element extends AnnotatedConstruct {

//获取父Element

Element getEnclosingElement();

//获取子Element的集合

List getEnclosedElements();

}

二、TypeMirror

============

Element有一个asType()方法用来返回TypeMirrorTypeMirror表示 Java 编程语言中的类型。这些类型包括基本类型、声明类型(类和接口类型)、数组类型、类型变量和 null 类型。还可以表示通配符类型参数、executable 的签名和返回类型,以及对应于包和关键字 void 的伪类型。我们一般用TypeMirror进行类型判断。如下段代码,用来比较元素所描述的类型是否是Activity的子类。

/**

  • 类型相关工具类

*/

private Types typeUtils;

/**

  • 元素相关的工具类

*/

private Elements elementUtils;

private static final String ACTIVITY_TYPE = “android.app.Activity”;

private boolean isSubActivity(Element element){

//获取当前元素的TypeMirror

TypeMirror elementTypeMirror = element.asType();

//通过工具类Elements获取Activity的Element,并转换为TypeMirror

TypeMirror viewTypeMirror = elementUtils.getTypeElement(ACTIVITY_TYPE).asType();

//用工具类typeUtils判断两者间的关系

return typeUtils.isSubtype(elementTypeMirror,viewTypeMirror)

}

三、一个简单的ButterKnife

==================

这一节我们通过编写一个简单的ButterKnife来介绍一下如何编写一个APT框架。APT应该是编译期插桩最简单的一种技术,通过三步就可以完成。

1、定义编译期注解。


我们新增一个Java Library Module命名为apt_api,编写注解类BindView。

@Retention(RetentionPolicy.Class)

@Target(ElementType.FIELD)

public @interface BindView {

}

这里简单介绍一下RetentionPolicyRetentionPolicy是一个枚举,它的值有三种:SOURCE、CLASS、RUNTIME。

  • SOURCE:不参与编译,让开发者使用。

  • CLASS:参与编译,运行时不可见。给编译器使用。

  • RUNTIME:参与编译,运行时可见。给编译器和JVM使用。

**2、**定义注解处理器。


同样,我们需要新增一个Java Library Module命名为apt_processor

我们需要引入两个必要的依赖:一个是我们新增的module apt_annotation,另一个是google的com.google.auto.service:auto-service:1.0-rc3(以下简称auto-service)。

implementation project(’:apt_api’)

api ‘com.google.auto.service:auto-service:1.0-rc3’

新增一个类 ButterKnifeProcessor,继承 AbstractProcessor

@AutoService(Processor.class)

public class ButterKnifeProcessor extends AbstractProcessor {

/**

  • 元素相关的工具类

*/

private Elements elementUtils;

/**

  • 文件相关的工具类

*/

private Filer filer;

/**

  • 日志相关的工具类

*/

private Messager messager;

/**

  • 类型相关工具类

*/

private Types typeUtils;

@Override

public Set getSupportedAnnotationTypes() {

return Collections.singleton(BindView.class.getCanonicalName());

}

@Override

public SourceVersion getSupportedSourceVersion() {

return SourceVersion.RELEASE_7;

}

@Override

public synchronized void init(ProcessingEnvironment processingEnvironment) {

super.init(processingEnvironment);

elementUtils = processingEnv.getElementUtils();

filer = processingEnv.getFiler();

messager = processingEnv.getMessager();

typeUtils = processingEnv.getTypeUtils();

}

@Override

public boolean process(Set set, RoundEnvironment roundEnvironment) {

return false;

}

}

auto-service为我们简化了定义注解处理器的流程。@AutoService是就是由auto-service提供的,其作用是用来告诉编译器我们定义的ButterKnifeProcessor是一个编译期注解处理器。这样在编译时ButterKnifeProcessor才会被调用。

我们还重写了AbstractProcessor提供的四个方法:getSupportedAnnotationTypesgetSupportedSourceVersioninitprocess

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

Android学习PDF+架构视频+面试文档+源码笔记

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

一篇文章教你如何在Android编译期插桩,让程序学会自己写代码_第4张图片

【算法合集】

一篇文章教你如何在Android编译期插桩,让程序学会自己写代码_第5张图片

【延伸Android必备知识点】

一篇文章教你如何在Android编译期插桩,让程序学会自己写代码_第6张图片

【Android部分高级架构视频学习资源】

oid%E5%BC%80%E5%8F%91%E4%B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-vpppHNTK-1643965526524)]

【算法合集】

[外链图片转存中…(img-6uzhQBdb-1643965526525)]

【延伸Android必备知识点】

[外链图片转存中…(img-Wji9eySx-1643965526525)]

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

你可能感兴趣的:(程序员,架构,移动开发,android)