插件化组件化 : APT : APT原理分析

https://blog.csdn.net/cpcpcp123/category_9620735.html

Q:APT原理是什么,怎么被执行起来的?

A:在javac编译时,先通过SPI机制加载所有的APT实现类,并将解析到需要编译的java源文件中所有的注解信息,与APT声明的支持处理的注解信息进行匹配,若待编译的源文件中存在APT声明的注解,则调起APT实现类的process方法。


Q:APT中process方法到底执行几次?为什么这么设计?

A:最少执行两次,最多无数次。最后一次执行可以视为Finish结束通知,执行收尾工作。


Q:APT中process方法boolean返回值返回true或者false有什么影响?

A: true:声明的注解只能被当前APT处理;false:在当前APT处理完声明的注解后,仍能被其他APT处理

1:不同module 之间Activity跳转处理器

1.1:定义注解

针对BBS和shop以及主module各模块之间跳转,我们定义四个注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface AutoRouter {
}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Component {
    String value();
}


@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Components {
    String[] value();
}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface StaticRouter {
    String value();
}



1.2:编码处理器RouterProcessor

@AutoService(Processor.class)
public class RouterProcessor extends AbstractProcessor {

    private Filer mFiler;
    private Messager mMessager;

    private Map<String, String> mStaticRouterMap = new HashMap<>();
    private List<String> mAutoRouterList = new ArrayList<>();

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
        mMessager = processingEnvironment.getMessager();
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new HashSet<>();
        set.add(AutoRouter.class.getCanonicalName());
        set.add(StaticRouter.class.getCanonicalName());
        set.add(Component.class.getCanonicalName());
        set.add(Components.class.getCanonicalName());
        return set;
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        mStaticRouterMap.clear();
        mAutoRouterList.clear();

        try {
            Set<? extends Element> mainAppElement = roundEnvironment.getElementsAnnotatedWith(Components.class);
            if (!mainAppElement.isEmpty()) {
                processInstaller(mainAppElement);
                return true;// 第一轮执行APT (创建 APP module对应的 注解类)
            }
            // 第二轮或者第三轮执行APT (创建 bbslib 和 shoplib module对应的 注解类)
            processComponent(roundEnvironment);
        } catch (Exception e) {
            mMessager.printMessage(Diagnostic.Kind.ERROR, e.getMessage());
        }

        return true;
    }

    private void processInstaller(Set<? extends Element> mainAppElement) throws IOException {
        TypeElement typeElement = (TypeElement) mainAppElement.iterator().next();
        JavaFileObject javaFileObject = mFiler.createSourceFile(Config.ROUTER_MANAGER, typeElement); // RouterManager
        PrintWriter writer = new PrintWriter(javaFileObject.openWriter());

        writer.println("package " + Config.PACKAGE_NAME + ";"); //  org.loader.router
        writer.println("import org.loader.router.Router_shop;");// 导包 router_shop
        writer.println("import org.loader.router.Router_bbs;");// 导包 Router_bbs
        writer.println("public class " + Config.ROUTER_MANAGER + " {"); // RouterManager
        writer.println("public static void " + Config.ROUTER_MANAGER_METHOD + "() {");// setup

        Components componentsAnnotation = typeElement.getAnnotation(Components.class);
        String[] components = componentsAnnotation.value();
        for (String item : components) {
            writer.println(Config.FILE_PREFIX + item + ".router();"); //Router_
        }

        writer.println("}");
        writer.println("}");

        writer.flush();
        writer.close();
    }

    private void processComponent(RoundEnvironment roundEnvironment) throws Exception {
        Set<? extends Element> compElements = roundEnvironment.getElementsAnnotatedWith(Component.class);
        if (compElements.isEmpty()) { return;}

        Element item = compElements.iterator().next();
        String componentName = item.getAnnotation(Component.class).value();

        Set<? extends Element> routerElements = roundEnvironment.getElementsAnnotatedWith(StaticRouter.class);
        for (Element e : routerElements) {
            if (! (e instanceof TypeElement)) { continue;}
            TypeElement typeElement = (TypeElement) e;
            String pattern = typeElement.getAnnotation(StaticRouter.class).value();
            mStaticRouterMap.put(pattern, typeElement.getQualifiedName().toString());
        }

        Set<? extends Element> autoRouterElements = roundEnvironment.getElementsAnnotatedWith(AutoRouter.class);
        for (Element e : autoRouterElements) {
            if (!(e instanceof TypeElement)) { continue;}
            TypeElement typeElement = (TypeElement) e;
            mAutoRouterList.add(typeElement.getQualifiedName().toString());
        }

        writeComponentFile(componentName);
    }

    private void writeComponentFile(String componentName) throws Exception {
        String className = Config.FILE_PREFIX + componentName;// Router_
        JavaFileObject javaFileObject = mFiler.createSourceFile(className);
//        javaFileObject.delete();

        PrintWriter printWriter = new PrintWriter(javaFileObject.openWriter());

        printWriter.println("package " + Config.PACKAGE_NAME + ";");

        printWriter.println("import android.app.Activity;");
        printWriter.println("import android.app.Service;");
        printWriter.println("import android.content.BroadcastReceiver;");

        printWriter.println("public class " + className + " {");
        printWriter.println("public static void router() {");

        // // Router.router(ActivityRule.ACTIVITY_SCHEME + "shop.main", ShopActivity.class);
        for(Map.Entry<String, String> entry : mStaticRouterMap.entrySet()) {
            printWriter.println("org.loader.router.Router.router(\"" + entry.getKey()
                    +"\", "+entry.getValue()+".class);");
        }

        for (String klass : mAutoRouterList) {
            printWriter.println("if (Activity.class.isAssignableFrom(" + klass + ".class)) {");
            printWriter.println("org.loader.router.Router.router(org.loader.router.rule.ActivityRule.ACTIVITY_SCHEME + \""
                    +klass+"\", " + klass + ".class);");
            printWriter.println("}");

            printWriter.println("else if (Service.class.isAssignableFrom(" + klass + ".class)) {");
            printWriter.println("org.loader.router.Router.router(org.loader.router.rule.ServiceRule.SERVICE_SCHEME + \""
                    +klass+"\", " + klass + ".class);");
            printWriter.println("}");

            printWriter.println("else if (BroadcastReceiver.class.isAssignableFrom(" + klass + ".class)) {");
            printWriter.println("org.loader.router.Router.router(org.loader.router.rule.ReceiverRule.RECEIVER_SCHEME + \""
                    +klass+"\", "+klass+".class);");
            printWriter.println("}");
        }

        printWriter.println("}");
        printWriter.println("}");
        printWriter.flush();
        printWriter.close();
    }

1.3: 生成APT源码

1.3.1 在APP主module中生成 RouterManager.java源码

package org.loader.router;
import org.loader.router.Router_shop;
import org.loader.router.Router_bbs;
public class RouterManager {
public static void setup() {
Router_shop.router();
Router_bbs.router();
}
}

1.3.2:在Shop module中,生成Router_shop.java源码

package org.loader.router;
import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
public class Router_shop {
public static void router() {
org.loader.router.Router.router("activity://shop.main", org.loader.shoplib.ShopActivity.class);
}
}

1.3.3:在bbs module中,生成Router_bbs.java源码

package org.loader.router;
import android.app.Activity;
import android.app.Service;
import android.content.BroadcastReceiver;
public class Router_bbs {
public static void router() {
if (Activity.class.isAssignableFrom(org.loader.bbslib.BBSActivity.class)) {
org.loader.router.Router.router(org.loader.router.rule.ActivityRule.ACTIVITY_SCHEME + "org.loader.bbslib.BBSActivity", org.loader.bbslib.BBSActivity.class);
}
else if (Service.class.isAssignableFrom(org.loader.bbslib.BBSActivity.class)) {
org.loader.router.Router.router(org.loader.router.rule.ServiceRule.SERVICE_SCHEME + "org.loader.bbslib.BBSActivity", org.loader.bbslib.BBSActivity.class);
}
else if (BroadcastReceiver.class.isAssignableFrom(org.loader.bbslib.BBSActivity.class)) {
org.loader.router.Router.router(org.loader.router.rule.ReceiverRule.RECEIVER_SCHEME + "org.loader.bbslib.BBSActivity", org.loader.bbslib.BBSActivity.class);
}
}
}

2:仿ButterKnife给Activity各控件绑定Id

2.1 : 定义注解

// 保留到编译阶段,不加载进JVM
@Retention(RetentionPolicy.CLASS)
// 作用对象
@Target(ElementType.FIELD)
public @interface BindView {
    int value(); // 属性名为value的int 变量
}

2.2 :编码注解处理器

@SuppressWarnings("unused")
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
    // Element代表程序的元素,例如包、类、方法。
    private Elements elementUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
    }

    /**
     * @return 指定java版本。
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 指定该目标的注解对象,指定注解处理器是注册给哪个注解的,返回指定支持的注解类集合。
     *
     * @return Set<String> getCanonicalName即包名.类名,不同的对象获取的值不同,可能为空
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> hashSet = new HashSet<>();
        hashSet.add(BindView.class.getCanonicalName());
        return hashSet;
    }

    /**
     * 处理包含指定注解对象的代码元素
     * 获取控件变量的引用以及对应的viewId,先遍历出每个Activity所包含的所有注解对象
     *
     * @param set              Set<? extends TypeElement>
     * @param roundEnvironment RoundEnvironment 所有注解的集合
     * @return true
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 获取所有包含BindView注解的元素
        Set<? extends Element> elementSet = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        // 此处的TypeElement就是Activity
        // Activity中包含的 id以及对应的属性(控件)
        Map<TypeElement, Map<Integer, VariableElement>> typeElementMapHashMap = new HashMap<>();
        for (Element element : elementSet) {
            // 注解的是FIELD,因此可以直接转换
            VariableElement variableElement = (VariableElement) element;
            // 获取最里层的元素,此处就是Activity
            TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
            // 获取对应Activity中的Map viewId View
            Map<Integer, VariableElement> variableElementMap = typeElementMapHashMap.get(typeElement);
            if (variableElementMap == null) {
                variableElementMap = new HashMap<>();
                typeElementMapHashMap.put(typeElement, variableElementMap);
            }
            // 获取注解对象
            BindView bindView = variableElement.getAnnotation(BindView.class);
            // 获取注解值
            int id = bindView.value();
            variableElementMap.put(id, variableElement);
        }

        for (TypeElement key : typeElementMapHashMap.keySet()) {
            Map<Integer, VariableElement> elementMap = typeElementMapHashMap.get(key);
            String packageName = elementUtils.getPackageOf(key).getQualifiedName().toString();

            JavaFile javaFile = JavaFile.builder(packageName, generateCodeByPoet(key,
                    elementMap)).build();
            try {
                javaFile.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return true;
    }

    private TypeSpec generateCodeByPoet(TypeElement typeElement, Map<Integer, VariableElement> variableElementMap) {
        //自动生成的文件以 Activity名 + ViewBinding 进行命名
        return TypeSpec.classBuilder(typeElement.getSimpleName().toString() + "ViewBinding")
                .addModifiers(Modifier.PUBLIC)
                .addMethod(generateMethodByPoet(typeElement, variableElementMap))
                .build();
    }

    /**
     * @param typeElement        注解对象的根元素,即Activity
     * @param variableElementMap Activity包含的注解对象以及注解的目标对象
     * @return MethodSpec
     */
    private MethodSpec generateMethodByPoet(TypeElement typeElement, Map<Integer, VariableElement> variableElementMap) {
        ClassName className = ClassName.bestGuess(typeElement.getQualifiedName().toString());
        //  _mainActivity.btn_serializeSingle = (android.widget.Button) (_mainActivity.findViewById(2131165221));
        // 第一个转小写+下划线
        String parameter = "_" + Utils.toLowerCaseFirstChar(className.simpleName());
        MethodSpec.Builder builder = MethodSpec.methodBuilder("bind") // 方法名
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)             // public static
                .returns(void.class)// 返回类型
                .addParameter(className, parameter);
        for (int viewId : variableElementMap.keySet()) {
            VariableElement variableElement = variableElementMap.get(viewId);
            // 变量名
            String fieldName = variableElement.getSimpleName().toString();
            // 变量父类的全称
            String fieldType = variableElement.asType().toString();
            String text = "{0}.{1} = ({2})({3}.findViewById({4}));";
            builder.addCode(MessageFormat.format(text, parameter, fieldName, fieldType, parameter, String.valueOf(viewId)));
        }
        return builder.build();
    }
}

2.3 生成注解文件

package org.loader.module2module;

public class MainActivityViewBinding {
  public static void bind(MainActivity _mainActivity) {
    _mainActivity.tv = (android.widget.TextView)(_mainActivity.findViewById(2131165313));}
}

3: 项目工程代码结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值