AOP思想实践之APT

2021-08-24  本文已影响0人  编程的猫

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思想很重要,掌握它的所有实现方式很重要!!!

上一篇下一篇

猜你喜欢

热点阅读