文章目录

  • 一、组件间共享的服务
  • 二、注解处理器添加对上述 " 组件间共享的服务 " 的支持
  • 三、注解处理器 生成代码规则
  • 四、完整注解处理器代码 及 生成的 Java 代码
    • 1、注解处理器代码
    • 2、app 模块中的注解类生成的 Java 源码
    • 3、library2 模块中的注解类生成的 Java 源码
  • 五、博客资源

组件化系列博客 :

  • 【Android 组件化】从模块化到组件化
  • 【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )
  • 【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )
  • 【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )
  • 【Android 组件化】路由组件 ( 路由组件结构 )
  • 【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
  • 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )
  • 【Android 组件化】路由组件 ( 注解处理器参数选项设置 )
  • 【Android 组件化】路由组件 ( 构造路由表中的路由信息 )
  • 【Android 组件化】路由组件 ( 使用 JavaPoet 生成路由表类 )

一、组件间共享的服务


路由除了支持 Activity 之外 , 还要支持 组件间共享的服务 如 工具类 , 逻辑功能 等 ;

注意 : 这里的 " 组件间共享的服务 " 不是 444 大组件中的 Service 组件 , 是 任意的 , 实现了 IService 接口的 Java 类 , 可以是工具类 , 业务逻辑 , 等等 ;

定义空的接口 IService , 令 需要共享的服务类 实现接口 , 该接口没有实际的意义 , 仅用于标记该接口需要纳入路由组件管理 , 起标记作用 ;

package kim.hsl.route_core.template;/*** 需要跨组件通信的服务需要实现该接口* 用于标记服务*/
public interface IService {}

接口定义位置 :

跨组件调用时 , 需要暴露出一个接口 , 接口必须实现上述 IService 接口 , 用于作为标识 , 注解处理器中 , 通过判断该注解节点的类型是不是该接口的子类 , 如果是则生成 路由信息 , 加入到 路由表 中 ;

IService 接口仅用与 标识 服务是否在 组件间共享 ;

针对每个具体的服务 , 还要在 底层依赖库 中定义一系列的接口 , 这里的底层依赖库是所有的 Module 组件都要依赖的 Android Library Module 依赖库 ;

在其中定义一个接口 ComponentService , 继承 IService 接口 , 在该接口中定义一系列需要暴露的方法 ;

package kim.hsl.base;import kim.hsl.route_core.template.IService;/*** 暴露一个接口*/
public interface ComponentService extends IService {void doSomething();
}

该接口定义位置 : 所有的组件都依赖 base 依赖库 ;

在具体的组件中 , 实现上述 ComponentService 接口 , 并添加 @Route 注解 ;

package kim.hsl.library2;import android.util.Log;import kim.hsl.base.ComponentService;
import kim.hsl.router_annotation.Route;@Route(path = "/library2/StringService")
public class StringService implements ComponentService {@Overridepublic void doSomething() {Log.i("StringService", "library2 组件中的 StringService 服务 ");}
}

该类定义位置 : 在任意模块都可以调用该类 ;

二、注解处理器添加对上述 " 组件间共享的服务 " 的支持


之前在注解处理器中 , 只支持 android.app.Activity 的路由节点添加 ;

// 获取 android.app.Activity 类型的注解节点
TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity");// 判断 typeMirror 注解节点是否是 Activity 类型
if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) {// 该节点是 android.app.Activity 类型的routeBean = new RouteBean(RouteBean.Type.ACTIVITY,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组
}

当前注解处理器中 , 支持 kim.hsl.route_core.template.IService 类型的 路由节点 添加 ;

// 获取 android.app.Activity 类型的注解节点
TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity");
// 获取 组件间共享服务 的接口, 该接口仅用于表示组件类型
TypeElement iServiceElement = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService");// 判断 typeMirror 注解节点是否是 Activity 类型
if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) {// 该节点是 android.app.Activity 类型的routeBean = new RouteBean(RouteBean.Type.ACTIVITY,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组
}else if (mTypeUtils.isSubtype(element.asType(), iServiceElement.asType())) {// 该节点是 kim.hsl.route_core.template.IService 类型的routeBean = new RouteBean(RouteBean.Type.ISERVICE,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组
}else{// 该节点不是 android.app.Activity 类型的throw new RuntimeException("@Route 注解节点类型错误");
}

三、注解处理器 生成代码规则


注解处理器的 process 方法调用 , 是按照 Module 模块进行的 ;

如果 Module 模块中有相关注解 , 传入的 Set<? extends TypeElement> set 参数不为空 , 就会进行相关处理 ;

如果 Module 模块中没有相关注解 , 传入的 Set<? extends TypeElement> set 参数为空 , 此时就不进行后续操作 ;

下图红色的 library1 模块中没有注解 ;

蓝色的 library2 模块中添加了 @Route(path = “/library2/StringService”) 注解 ;

绿色的 app 模块中添加了 @Route(path = “/app/MainActivity”) 注解 ;

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

四、完整注解处理器代码 及 生成的 Java 代码


1、注解处理器代码

package kim.hsl.router_compiler;import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;import kim.hsl.router_annotation.Route;
import kim.hsl.router_annotation.model.RouteBean;import static javax.lang.model.element.Modifier.PUBLIC;// 注解处理器接收的参数
@SupportedOptions("moduleName")
// 自动注册注解处理器
@AutoService(Processor.class)
// 支持的注解类型
@SupportedAnnotationTypes({"kim.hsl.router_annotation.Route"})
// 支持的 Java 版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class RouterProcessor extends AbstractProcessor {/*** 注解处理器中使用 Messager 对象打印日志*/private Messager mMessager;/*** 用于写出生成的 Java 代码*/private Filer mFiler;/*** 注解节点工具*/private Elements mElementUtils;/*** 类工具*/private Types mTypeUtils;/*** 获取的 moduleName 参数*/private String mModuleName;/*** 管理路由信息* 键 ( Key ) : 路由分组名称* 值 ( Value ) : 路由信息集合*/private HashMap<String, ArrayList<RouteBean>> mGroupMap = new HashMap<>();/*** 管理 路由表信息* 键 ( Key ) : 组名* 值 ( Value ) : 类名*/private Map<String, String> mRootMap = new TreeMap<>();/*** 该函数在初始化时调用 , 相当于构造函数* @param processingEnvironment*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);// 获取打印日志接口this.mMessager = processingEnvironment.getMessager();// 测试日志打印mMessager.printMessage(Diagnostic.Kind.NOTE, "Messager Print Log");this.mFiler = processingEnvironment.getFiler();this.mElementUtils = processingEnvironment.getElementUtils();this.mTypeUtils = processingEnvironment.getTypeUtils();// 获取 moduleName 参数// 先获取 注解处理器 选项Map<String, String> options = processingEnvironment.getOptions();if (options != null){mModuleName = options.get("moduleName");mMessager.printMessage(Diagnostic.Kind.NOTE, "打印 moduleName 参数 : " + mModuleName);}}/*** 该函数在注解处理器注册时自动执行, 是处理注解的核心函数** Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合** @param set* @param roundEnvironment* @return*/@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {if (set == null || set.isEmpty()){// 如果没有检测到注解 , 直接退出return false;}// 获取被 @Route 注解的节点// 这些 注解节点 都是类节点 , TypeElement 类型的Set<? extends Element> routeElements = roundEnvironment.getElementsAnnotatedWith(Route.class);generateRouteClass(routeElements);// 生成 路由组件 分组表 对应的 Java 类generateGroupTable();// 生成 路由组件 路由表 对应的 Java 类return true;}/*** 生成 路由组件 分组表 对应的 Java 类*/private void generateGroupTable() {// 获取要生成的类 需要实现的接口节点TypeElement iRouteGroup = mElementUtils.getTypeElement("kim.hsl.route_core.template.IRouteGroup");// 打印类节点全类名mMessager.printMessage(Diagnostic.Kind.NOTE,"打印 路由表 需要实现的接口节点 iRouteGroup : " + iRouteGroup.getQualifiedName());// 生成参数类型 Map<String, RouteBean> atlasParameterizedTypeName atlasType = ParameterizedTypeName.get(ClassName.get(Map.class),ClassName.get(String.class),ClassName.get(RouteBean.class));// 生成参数 Map<String, RouteBean> atlasParameterSpec atlasValue = ParameterSpec.builder(atlasType, "atlas").build();// 遍历 HashMap<String, ArrayList<RouteBean>> mGroupMap = new HashMap<>() 路由分组// 为每个 路由分组 创建一个类for (Map.Entry<String, ArrayList<RouteBean>> entry : mGroupMap.entrySet()){// 创建函数 loadIntoMethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("loadInto").addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).addParameter(atlasValue);// 函数体中的代码生成// 获取 ArrayList<RouteBean> 数据ArrayList<RouteBean> groupRoutes = entry.getValue();// 组名String groupName = "";// 生成函数体代码for (RouteBean routeBean : groupRoutes){// 获取组名groupName = routeBean.getRouteGroup();// $S 表示字符串// $T 表示类// $L 表示字面量 , 原封不动的字符串替换methodBuilder.addStatement("atlas.put($S, new $T($T.$L, $T.class, $S, $S))",// $S 字符串 : "main"routeBean.getRouteGroup(),// $T 类名 : RouteBeanClassName.get(RouteBean.class),// $T 类名 : TypeClassName.get(RouteBean.Type.class),// $L 字面量 : ACTIVITYrouteBean.getType(),// $T 类名 : kim.hsl.component.MainActivity 类ClassName.get((TypeElement) routeBean.getElement()),// $S 字符串 : "/app/MainActivity"routeBean.getRouteAddress(),// $S 字符串 : "app"routeBean.getRouteGroup());}// 创建类// 构造类名  Router_Group_mainString groupClassName = "Router_Group_" + groupName;// 创建类TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName).addSuperinterface(ClassName.get(iRouteGroup)).addModifiers(PUBLIC).addMethod(methodBuilder.build()).build();// 生成 Java 源码文件JavaFile javaFile = JavaFile.builder("kim.hsl.router", typeSpec).build();// 将 Java 源文件写出到相应目录中try {mMessager.printMessage(Diagnostic.Kind.NOTE,"输出文件 : " + groupClassName);javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();mMessager.printMessage(Diagnostic.Kind.NOTE,"输出文件出现异常");}finally {mMessager.printMessage(Diagnostic.Kind.NOTE,"输出文件完毕");}// 统计路由表信息mRootMap.put(groupName, groupClassName);}}private void generateRouteClass(Set<? extends Element> routeElements) {// 获取 android.app.Activity 类型的注解节点TypeElement activityElement = mElementUtils.getTypeElement("android.app.Activity");// 获取 组件间共享服务 的接口, 该接口仅用于表示组件类型TypeElement iServiceElement = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService");// 处理 @Route(path = "app/MainActivity") 节点for (Element element : routeElements) {// 获取 Route 注解Route route = element.getAnnotation(Route.class);// 路由表中的单个路由对象RouteBean routeBean = null;// 判断 typeMirror 注解节点是否是 Activity 类型if (mTypeUtils.isSubtype(element.asType(), activityElement.asType())) {// 该节点是 android.app.Activity 类型的routeBean = new RouteBean(RouteBean.Type.ACTIVITY,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组}else if (mTypeUtils.isSubtype(element.asType(), iServiceElement.asType())) {// 该节点是 kim.hsl.route_core.template.IService 类型的routeBean = new RouteBean(RouteBean.Type.ISERVICE,    // 路由对象类型element,         // 路由节点null,    // 类对象route.path(),   // 路由地址route.group()); // 路由组}else{// 该节点不是 android.app.Activity 类型的throw new RuntimeException("@Route 注解节点类型错误");}// 检查路由地址checkRouteAddress(routeBean);// 打印路由信息mMessager.printMessage(Diagnostic.Kind.NOTE,"打印路由信息 : " + routeBean.toString());// 处理路由信息分组routeGroup(routeBean);}}/*** 处理路由信息分组* @param routeBean*/private void routeGroup(RouteBean routeBean) {// 首先从 groupMap 集合中获取该分组的所有 路由信息ArrayList<RouteBean> routeBeans = mGroupMap.get(routeBean.getRouteGroup());if (routeBeans == null){// 如果从 mGroupMap 获取的该分组的路由信息集合为空// 则创建新集合, 放置路由信息, 并加入到 mGroupMap 中routeBeans = new ArrayList<>();routeBeans.add(routeBean);mGroupMap.put(routeBean.getRouteGroup(), routeBeans);}else{// 从 mGroupMap 获取的路由分组对应的路由信息集合不为空// 直接添加 路由信息 即可routeBeans.add(routeBean);}}/*** 验证路由地址* @Route(path = "/app/MainActivity")* @param routeBean*/private void checkRouteAddress(RouteBean routeBean){// 获取路由地址String routeAddress = routeBean.getRouteAddress();// 获取路由分组String routeGroup = routeBean.getRouteGroup();// 验证路由地址是否以 "/" 开头if (!routeAddress.startsWith("/")) {throw new RuntimeException("路由地址 " + routeAddress + " 格式错误");}// 如果路由地址的分组为空 ,// 则截取第 0 和 第 1 个 "/" 之间的字符串作为分组名称if (routeGroup == null || "".equals(routeGroup)){String group = routeAddress.substring(routeAddress.indexOf("/", 0) + 1,routeAddress.indexOf("/", 1));if (group == null || "".equals(group)){throw new RuntimeException("路由地址 " + routeAddress + " 获取分组错误");}// 打印组名mMessager.printMessage(Diagnostic.Kind.NOTE,"打印路由地址 " + routeAddress + " 的组名为 " + group);// 正式设置路由地址分组routeBean.setRouteGroup(group);}}
}

2、app 模块中的注解类生成的 Java 源码

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

app 中的注解类 :

@Route(path = "/app/MainActivity")
public class MainActivity extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
}

生成的源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\app\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_app.java ;

package kim.hsl.router;import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.component.MainActivity;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_app implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("app", new RouteBean(RouteBean.Type.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));}
}

3、library2 模块中的注解类生成的 Java 源码

Module 模块中 , 使用注解生成的源码 , 都在对应模块的 " build\generated\ap_generated_sources\debug\out\ " 目录中 ;

library2 中的注解类 :

package kim.hsl.library2;import android.util.Log;import kim.hsl.base.ComponentService;
import kim.hsl.router_annotation.Route;@Route(path = "/library2/StringService")
public class StringService implements ComponentService {@Overridepublic void doSomething() {Log.i("StringService", "library2 组件中的 StringService 服务 ");}
}

生成的源码 : 生成源码路径 D:\002_Project\002_Android_Learn\Component\library2\build\generated\ap_generated_sources\debug\out\kim\hsl\router\Router_Group_library2.java ;

package kim.hsl.router;import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.library2.StringService;
import kim.hsl.route_core.template.IRouteGroup;
import kim.hsl.router_annotation.model.RouteBean;public class Router_Group_library2 implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteBean> atlas) {atlas.put("library2", new RouteBean(RouteBean.Type.ISERVICE, StringService.class, "/library2/StringService", "library2"));}
}

五、博客资源


博客源码 :

  • GitHub : https://github.com/han1202012/Component
  • CSDN 下载 :

【Android 组件化】路由组件 ( 组件间共享的服务 )相关推荐

  1. calces组件化与ARouter组件间通信

    calces组件化与ARouter组件间通信 calces介绍: 属性介绍 ARouter(路由) https://github.com/Tangpj/calces-gradle-plugin cal ...

  2. 前端组件化:vue组件设计思想与遵从原则

    组件化的工作方式信奉独立.完整.自由组合.目标就是尽可能把设计与开发中的元素独立化,使它具备完整的局部功能,通过自由组合来构成整个产品. 从页面元素的可复用性角度考虑,我们将将组件按类型分为公众组件. ...

  3. android 组件化_Android 组件化路由框架设计(仿Arouter)

    前言 在组件化开发中一个必须要面对的问题就是组件间页面跳转,实现的方法有很多,简单的可以通过反射获取,但是比较耗费性能,也可以通过隐式跳转,但是随着页面的增多,过滤条件会随之增多,后期维护麻烦.那还有 ...

  4. android 组件化_你曾遇到的某大厂奇葩问题:Android组件化开发,组件间的Activity页面跳转...

    组件化开发有什么好处? 1.当项目越来越大时,app的业务越来越复杂,会出现业务功能复杂混乱,各功能块.页面相互依赖,相互调用太多导致耦合度高,而采用组件化开发,我们就可以将功能模块合理的划分,降低功 ...

  5. [Android]如何做一个崩溃率少于千分之三噶应用app(22)-组件化路由跳转

    大家好,我是苍王.以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章. [Android]如何做一个崩溃率少于千分之三噶应用app--章节列表 大家应该有看完我的第十二编简书 ...

  6. android 蘑菇街组件化,4. IOS 组件化(蘑菇街的路由+协议式)

    为了研究组件化,我们主要是讨论 蘑菇街的路由+协议式 和 中间件 讨论第一种方式,并参考 蘑菇街IOS组件化 ,我们来实现一个可以运行的demo,并讨论优缺点. 路由 用MGJRouter 单例,通过 ...

  7. iOS 组件化 —— 路由设计思路分析

    原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...

  8. React(04):React中的组件化及父子组件间的传值取值

    前言 接着前一篇继续学习React组件化 React(03):React中的JSX语法 正文 什么是组件化: 是从 UI 界面视图的角度 来进行分析的:把一些可复用的UI元素,抽离为单独的组件:便于项 ...

  9. 微信小程序组件、路由、组件通信、侦听器

    一.微信小程序组件 组件就是小程序页面的组成结构,与html在web网页开发中的作用一样,铺设页面.可以参考其他UI库,像elementUI,vantUI组件 组件是视图层的基本组成单元. 组件自带一 ...

最新文章

  1. mac 拷贝文件时报错 8060 解决方案
  2. 0x52. 动态规划 - 背包(习题详解 × 19)
  3. Even Parity UVA - 11464 (枚举)
  4. 火狐自动换行 有空格
  5. QT使用之 手指滑动 | 物理惯性继续滑动动画的实现,根据不同速度实现不同动画效果
  6. mount and fstab的使用(整理)
  7. 平衡二叉树,AVL树之图解篇
  8. [cerc2012][Gym100624B]20181013
  9. docker安装gamit_ubuntun10.10中安装gamit 10.40
  10. 多少天能学会php,如何在十天内学会php之第八天_php
  11. 01 Go实战仿百度云盘课程介绍
  12. SAP 固定资产模块上线配置
  13. 解决Server2008下远程桌面连接“由于没有终端服务器许可证服务器可以提供许可证”
  14. 如何让自己像打王者荣耀一样发了疯、拼了命、石乐志的学习?
  15. mysql怎么子查询_在mysql中如何进行子查询?
  16. 密码学实验_7_S盒创建(python 实现)
  17. 如何提高内存卡的读写速度
  18. vh布局移动端软键盘弹起改变高度问题
  19. PHP正则验证手机号
  20. 【好奇心驱动力】ESP8266从零开始折腾记录

热门文章

  1. extjs gridpanel滚动条问题显示数据不完整
  2. .h头文件 .lib库文件 .dll动态链接库文件关系
  3. 网站搭建 (第09天) 博客统计排行
  4. 『一本通』差分约束系统
  5. 2018.08.04 cogs2633. [HZOI 2016]数列操作e(线段树)
  6. python 3389爆破机
  7. POJ 3660 Cow Contest [Floyd]
  8. string find简析
  9. Python——反射
  10. 高并发第八弹:J.U.C起航(java.util.concurrent)