AOP思想实践之APT
AOP优势:利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP分类:
从织入的时机的角度看,可以分为源码阶段、class阶段、dex阶段、运行时织入。
对于前三项源码阶段、class阶段、dex织入,由于他们都发生在class加载到虚拟机前,我们统称为静态织入,
而在运行阶段发生的改动,我们统称为动态织入。
织入时机 | 技术框架 |
---|---|
静态织入 | APT,AspectJ、ASM、Javassit |
动态织入 | java动态代理,cglib、Javassit |
很多开源框架都是用了APT动态生成代码技术,例如:ARouter、ButterKnife
这里APT实践以手写一个简单的findViewById为例子。APT需要注解和反射的知识,请自行学习。
在Android工程中除了app module外,新建两个java library
java library一,例如名称annotationsLib,其build.gradle如下:
plugins {
id 'java-library'
}
// 控制台中文设置UTF-8
tasks.withType(JavaCompile){
options.encoding = "UTF-8"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
新建以下注解:
// 编译时注解
@Retention(RetentionPolicy.CLASS)
// 作用在类上
@Target(ElementType.TYPE)
public @interface ViewPath {
}
// 运行时注解
@Retention(RetentionPolicy.RUNTIME)
// 作用在变量上
@Target(ElementType.FIELD)
public @interface ViewId {
// viewId
int value() default 0;
}
新建java Library二,名称:compilerLib,其build.gradle如下:
plugins {
id 'java-library'
}
// 控制台中文设置UTF-8
tasks.withType(JavaCompile){
options.encoding = "UTF-8"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
dependencies{
// 编译时期进行注解处理
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
// 生成代码
implementation 'com.squareup:javapoet:1.11.1'
implementation project(':annotationsLib')
}
上边的auto-service依赖是自动注册注解,javapoet 依赖是方便使用api构建生成java代码文件。APT的核心代码如下:(注意:process方法会调用多次,多次生成相同的文件会抛出异常,只有第一次Set<? extends TypeElement> annotations才会有值,根据annotations中的注解类型判断我们当前要使用的目标注解)
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({Constance.VIEW_PATH, Constance.HELLO_POET})
public class ViewIdProcessor extends AbstractProcessor {
// 获取类的工具对象
private Elements elementUtils;
// 获取类型的工具对象
private Types typeUtils;
private Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
System.out.println("=============【进入了 init 方法】==============");
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
filer = processingEnv.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("=============【进入了 process 方法】==============");
if (annotations != null) {
for (TypeElement typeElement : annotations) {
String qualifiedName = typeElement.getQualifiedName().toString();
String simpleNam = typeElement.getSimpleName().toString();
System.out.println("== qualifiedName:" + qualifiedName + " -> simpleNam:" + simpleNam);
if (qualifiedName.equals(Constance.HELLO_POET)) {
// 生成HelloPoet文件
testHello();
}else if (qualifiedName.equals(Constance.VIEW_PATH)){
// 生成ViewPath文件
generateViewPath(annotations, roundEnv);
}
}
}
return true;
}
/**
* 生成ViewPath注解标识的类
*/
public void generateViewPath(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 获取指定注解类型修饰的所有类 这里指定的注解类型:ViewPath.class
Set<? extends Element> elementsAnnotatedWithSet = roundEnv.getElementsAnnotatedWith(ViewPath.class);
for (Element element : elementsAnnotatedWithSet) {
// 判断element类型是否为class
TypeElement typeElement = (TypeElement) element;
// 获取当前类中的所有成员
List<? extends Element> allMembers = elementUtils.getAllMembers(typeElement);
// 生成方法
MethodSpec.Builder builderMethod = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(TypeName.VOID)
.addParameter(ClassName.get(typeElement.asType()), "activity");
for (Element member : allMembers) {
// 获取类中成员上的所有注解
// List<? extends AnnotationMirror> allAnnotationMirrorsWithMember = elementUtils.getAllAnnotationMirrors(member);
// if (allAnnotationMirrorsWithMember!=null){
//
// }
// 获取成员上指定类型的注解,被该注解修饰的需要赋值
ViewId annotationViewId = member.getAnnotation(ViewId.class);
if (annotationViewId == null) continue;
String formatStatement = String.format("activity.%s = (%s) activity.getWindow().getDecorView().findViewById(%s)",
member.getSimpleName(), ClassName.get(member.asType()).toString(), annotationViewId.value());
builderMethod.addStatement(formatStatement);
}
// 构造类
TypeSpec buildClazz = TypeSpec.classBuilder(typeElement.getSimpleName() + "_BindView")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(builderMethod.build())
.build();
// 构造java文件内容 指定java文件生成的路径
JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), buildClazz).build();
try {
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 获取包名
* @param typeElement typeElement
* @return 返回包名
*/
private String getPackageName(TypeElement typeElement){
// Qualified 获取全路径名称
return elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
}
}
代码中的注释已经写的很详细。点击rebuild project后就会在对应的包下生成java文件,例如本文中的路径:com/pluginchildapp/aopTest/AopActivity_BindView.java
app module依赖annotationsLib和compilerLib,build.gradle如下:
dependencies {
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
// 依赖注解和注解处理器
implementation project(path: ':annotationsLib')
annotationProcessor project(path: ':compilerLib')
// 注解api调用库
implementation project(path: ':aop-coreLib')
}
另外再新建一个Android library,例如名字叫:aop-coreLib,同样在app module中进行依赖。在aop-coreLib中新建类ViewBinder,这个类的作用是为了给Activity中的成员赋值,看下边的代码就明白了。
ViewBinder的代码
public class ViewBinder {
@RequiresApi(api = Build.VERSION_CODES.O)
public static void inject(Object target) {
if (target == null) throw new NullPointerException("object is null");
String simpleName = target.getClass().getSimpleName();
String packageName = target.getClass().getPackage().getName();
String className = packageName + "." + simpleName + "_BindView";
try {
Class<?> aClass = Class.forName(className);
Method bindView = aClass.getMethod("bindView", target.getClass());
bindView.invoke(null, target);
} catch (Exception e) {
e.printStackTrace();
Log.e("==TAT", "出错:" + e.getMessage());
}
}
}
在Activity中使用:
@ViewPath
public class AopActivity extends AppCompatActivity {
@ViewId(value = R.id.tv)
TextView textView;
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aop);
ViewBinder.inject(this);
textView.setText("赋值啦。。。");
HelloPoetClazz.helloPoet("哈哈哈");
}
}
AOP思想很重要,掌握它的所有实现方式很重要!!!