Navigation深入浅出,到出神入化,再到实战改造(三)

改造Navigation

目标:
  1. 摒弃xml文件,用注解的方式管理路由节点。利用映射关系,动态生成路由节点配置文件
  2. 改造FragmentNavigator,替换replace(),使用show(),hint()方式,路由Fragement

自定义注解处理器

1. 配置

gradle配置

//生成Json文件工具类
api 'com.alibaba:fastjson:1.2.59'
//注解处理器配置工具 
api 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

如果想要注解处理器能够在编译器生成代码,需要做一个配置说明,这里有两种配置方法:
具体参考这篇文章:Java AbstractProcessor实现自定义ButterKnife

注解处理器基本用法

//auto.service:auto-service使用时要添加这个注解
@AutoService(Processor.class)
// 项目配置 当前正在使用的Java版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
//要处理的注解类型的名称(这里必须是完整的包名+类名
@SupportedAnnotationTypes({
   
   "org.devio.hi.nav_annotation.Destination"})
public class NavProcessor extends AbstractProcessor {
   
   
   
   @Override
    void init(ProcessingEnvironment processingEnv) {
   
   
        super.init(processingEnv);
      //处理器被初始化的时候被调用
    }
     
    
    boolean process(Set annotations, RoundEnvironment roundEnv) 
      //处理器处理自定义注解的地方
      return false
}

注解处理器的引用

//Kotkin项目用 kapt Java项目用 annotationProcessor 
kapt project(path:'nav-compiler')
api project(path:'nav-annotations')

下面会将用的方法做介绍, 关于更多注解处理器和相关知识,可参考这几篇文章:

Java进阶–编译时注解处理器(APT)详解

Java AbstractProcessor实现自定义ButterKnife

JavaPoet的使用指南

Android AutoService 组件化

2. 创建项目

创建项目

这个工程会默认生成Navigation+BottomNavigationView项目结构。项目内容比较简单。这里不过多介绍。我们就改造这个项目。

创建两个Java lib :
在这里插入图片描述

为什么需要创建Java库? 创建Java库是因为在使用自定义AbstractProcessor需要使用到javax包中的相关类和接口,这个在android库中并不存在,所以需要使用到Java库。

nav_compiler module下的build.gradle:

dependencies {
   
   
    implementation fileTree(dir: 'libs', includes: ['*,jar'])

    //自定义注解处理器相关依赖

    //Json工具类
    api 'com.alibaba:fastjson:1.2.59'
    //让自定义处理器在编译时 能够被唤醒 能够执行
    api 'com.google.auto.service:auto-service:1.0-rc6'
    //添加我们定义的注解lib依赖
    implementation project(path: ':nav_annotation')
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

}

nav_annotation module下创建注解文件:

/**
 * 自定义注解,将这个注解
 * 注释到我们的要路由的类上面
 * 这样我们就可以获取配置的节点(e.g Activity/Fragment/Dialog)
 * 然后利用代码生成节点配置,替换掉nav_graph.xml;
 */
@Target(ElementType.TYPE)//类作用域
@Retention(RetentionPolicy.CLASS)//编译期生效
public @interface Destination {
   
   

    /**
     * 页面在路由中的名称
     */
    String pareUrl();

    /**
     * 节点是不是默认首次启动页
     */
    boolean asStarter() default false;
}

在这里我们有必要认识一下什么是Element。 在Java语言中,Element是一个接口,表示一个程序元素,它可以指代包、类、方法或者一个变量。Element已知的子接口有如下几种:

  • PackageElement 表示一个包程序元素。提供对有关包及其成员的信息的访问。
  • ExecutableElement 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。
  • TypeElement 表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。注意,枚举类型是一种类,而注解类型是一种接口。
  • VariableElement 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。

注解解释器具体代码如下:

/**
 * @Author :ggxz
 * @Date: 2022/3/5
 * @Desc:
 */
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({
   
   "org.devio.hi.nav_annotation.Destination"})
public class NavProcessor extends AbstractProcessor {
   
   
    private static final String PAGE_TYPE_ACTIVITY = "Activity";
    private static final String PAGE_TYPE_FRAGMENT = "Fragment";
    private static final String PAGE_TYPE_DIALOG = "Dialog";
    private static final String OUTPUT_FILE_NAME = "destination.json";

    private Messager messager;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
   
   
        super.init(processingEnv);

        //日志打印工具类
        messager = processingEnv.getMessager();
        messager.printMessage(Diagnostic.Kind.NOTE, "enter init...");

        //创建打印文件
        filer = processingEnv.getFiler();


    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
   
   
        //获取代码中所有使用@Destination 注解的类或字段
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(Destination.class);
        if (!elementsAnnotatedWith.isEmpty()) {
   
   
            Map<String, JSONObject> destMap = new HashMap<>();
            handleDestination(elementsAnnotatedWith, destMap, Destination.class);

            try {
   
   
                //创建资源文件
                FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "", OUTPUT_FILE_NAME);
                // 获取创建资源文件默认路径: .../app/build/intermediates/javac/debug/classes/目录下
                // 希望存放的目录为: /app/main/assets/
                String resourcePath = resource.toUri().getPath();
                //  获取 .../app 之前的路径
                String appPath = resourcePath.substring(0, resourcePath.indexOf("app") + 4);

                String assetsPath = appPath + "src/main/assets"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值