模仿组件化路由ARouter的原理(三)

本文介绍了创建Java注解和注解处理器的方法,包括创建Java模块、添加依赖、创建BindPath类和AnnotationCompiler等。还讲述了在ARouter的init方法中添加有BindPath注解的Activity到集合,实现注解值与对应class的映射,最后给出了实现原理的demo链接。

这一篇,我们来学习怎么自己创建注解和注解处理器。

首先创建两个java的module,一定要是java的。

然后所有的module都添加这两个依赖

注意,对于annotions_compiler 的依赖方式,要annotationProcessor

implementation project(':annotations')
annotationProcessor project(':annotions_compiler')

在annotation里创建一个BindPath类

/**
 * 定义注解需要用@interface声明,并且需要写原注解@Target(ElementType.TYPE)和@Retention(RetentionPolicy.CLASS)
 * 注解需要定义javaLib
 */
@Target(ElementType.TYPE)//声明这个注解放在什么上面的 TYPE是类上面
@Retention(RetentionPolicy.CLASS)//声明这个注解的生命周期   三个生命周期  java--->class--->run
public @interface BindPath {
    String value();
}

然后就是注解处理器,对于注解处理器,我们要特别注意一下

这是annotation_compiler的build.gradle,我们需要根据自己开发环境的不同来选择不同的google依赖。

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    
    //As  3.4+
//    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
//    compileOnly 'com.google.auto.service:auto-sevice:1.0-rc4'
    
    //依赖google 注解处理器服务包
    implementation 'com.google.auto.service:auto-service:1.0-rc3'
    implementation project(':annotations')
}

然后创建AnnotationCompiler

/**
 * 注解处理器
 * AbstractProcessor父类
 */
@AutoService(Processor.class)//注册注解处理器
public class AnnotionCompiler extends AbstractProcessor {

    //生成文件的对象
    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    /**
     * 声明我们这个注解处理器需要处理哪些注解
     *
     * @return
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();

        types.add(BindPath.class.getCanonicalName());

        return types;
    }

    /**
     * 声明我们支持的java版本
     *
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return processingEnv.getSourceVersion();
    }

    /**
     * 这个方法是注解处理的核心方法,写文件就放在这里进行写  element节点  TypeElement就是类节点
     *
     * @param annotations
     * @param roundEnv
     * @return
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        //通过这个API能拿到所有这个模块中所有的用到了BindPath注解的节点
        Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(BindPath.class);


        Map<String, String> map = new HashMap<>();
        for (Element element : elementsAnnotatedWith) {
            //找到类节点
            TypeElement typeElement = (TypeElement) element;
            //获取到activity集合的key
            String key = typeElement.getAnnotation(BindPath.class).value();//找到key BindPath()中的值
            String value = typeElement.getQualifiedName().toString();//带路径的名字
            map.put(key, value);
        }

        if (map.size() > 0) {
            Writer writer = null;
            String utilName = "ActivityUtils" + System.currentTimeMillis();
            try {
                JavaFileObject sourceFile = filer.createSourceFile("com.tengxincar.mobile.utils." + utilName);
                writer = sourceFile.openWriter();
                writer.write("package com.tengxincar.mobile.utils;\n" +
                        "\n" +
                        "import com.tengxincar.mobile.arouter.ARouter;\n" +
                        "import com.tengxincar.mobile.arouter.IRouter;\n" +
                        "\n" +
                        "public class " + utilName + " implements IRouter {\n" +
                        "    @Override\n" +
                        "    public void putActivity() {\n");
                Iterator<String> iterator = map.keySet().iterator();
                while (iterator.hasNext()) {
                    String key = iterator.next();
                    String value = map.get(key);
                    writer.write("ARouter.getInstance().putActivity(\"" + key + "\"," + value + ".class);\n");
                }
                writer.write("}\n}");

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (writer != null) {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


        return false;
    }
}

这个注解处理器,其实大部分内容是固定的,不固定的大概就是process这个方法,里面用了各种不熟悉的api,大致就是通过writer写java文件,这个java文件就是上一篇里的我们需要在每个module里自己写的那个,其中我觉得比较厉害的点在找到类节点,然后获取Annotation的value,并且获取到类节点带路径的值,可能是自己java基础不太好,我只能理解,其他的代码我也都作了注释,只能说大神都好牛逼啊。

注解和注解处理器都写完了,我们继续回到之前的ARouter,我们在ARouter的init方法里来把所有有BindPath注解的Activity都添加到集合中

public void init(Context context) {
    this.context = context;
    //包名下面所有的类名
    List<String> classNames = getClassName("com.tengxincar.mobile.utils");
    for (String className : classNames) {
        try {
            //得到类
            Class<?> aClass = Class.forName(className);
            //是否是IRouter的实现类
            if (IRouter.class.isAssignableFrom(aClass)) {
                //接口的引用指向子类的实例
                IRouter iRouter = (IRouter) aClass.newInstance();
                iRouter.putActivity();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
private List<String> getClassName(String packgeName) {
    //创建一个Class对象的集合
    List<String> classList = new ArrayList<>();
    String path = null;

    try {
        //通过包管理器,获取到应用信息类然后获取到APK的完整路径
        path = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).sourceDir;
        //根据APK的完整路径获取到编译后的dex文件
        DexFile dexFile = new DexFile(path);
        //获得编译后的dex文件中的所有class
        Enumeration enumeration = dexFile.entries();
        //然后进行遍历
        while (enumeration.hasMoreElements()) {
            //遍历所有的class包名
            String name = (String) enumeration.nextElement();
            //判断类的包名是否符合
            if (name.contains(packgeName)) {
                classList.add(name);
            }
        }


    } catch (Exception e) {
        e.printStackTrace();
    }
    return classList;
}

这里面我觉得比较关键的点就是getClassName方法,老师说这个方法网上都有,但是我还是不会写的啊,emmmm

然后就是这个地方我觉得比较牛逼

if (IRouter.class.isAssignableFrom(aClass)) {
    //接口的引用指向子类的实例
    IRouter iRouter = (IRouter) aClass.newInstance();
    iRouter.putActivity();
}

我们创建的java类都实现了IRouter的接口,然后就可以调用java类里的putActivity,从而实现真正的把注解值和对应的class放到我们的Map集合中。

然后接下来就是在我们LoginModule里添加注解

@BindPath("loginmodle/LoginModule")
public class LoginModuleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login_module);
    }

    public void jumpToPersonal(View view) {
        ARouter.getInstance().jumpToActivity("personalmodule/personalmodule", null);
    }
}

然后实现跳转。

 

其实这并不是真的ARouter,这大概是原理,我就是个老白(老+小白),跟着大神学,随手记录吧

最后贴一个github,里面有我实现的demo,欢迎交流。
(这是网易的公开课,网易大大不会告我侵权吧,逃。)

https://github.com/wangxueshen/NetEaseDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值