文章目录

  • 一、参数自动注入
  • 二、自定义注解
  • 三、使用 @Extra 自定义注解
  • 四、注解处理器解析 @Extra 自定义注解 并生成相应 Activity 对应代码
  • 五、博客资源

组件化系列博客 :

  • 【Android 组件化】从模块化到组件化
  • 【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )
  • 【Android 组件化】使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )
  • 【Android 组件化】使用 Gradle 实现组件化 ( 组件 / 集成模式下的 Library Module 开发 )
  • 【Android 组件化】路由组件 ( 路由组件结构 )
  • 【Android 组件化】路由组件 ( 注解处理器获取被注解的节点 )
  • 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )
  • 【Android 组件化】路由组件 ( 注解处理器参数选项设置 )
  • 【Android 组件化】路由组件 ( 构造路由表中的路由信息 )
  • 【Android 组件化】路由组件 ( 使用 JavaPoet 生成路由表类 )
  • 【Android 组件化】路由组件 ( 组件间共享的服务 )
  • 【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )
  • 【Android 组件化】路由组件 ( 运行时获取 注解处理器 生成的路由表 )
  • 【Android 组件化】路由组件 ( 路由框架概述 )
  • 【Android 组件化】路由组件 ( 页面跳转参数传递注解 )

一、参数自动注入


在 组件化 中 , 使用 路由组件 进行界面跳转时 , 涉及到参数的传递 , 传递过去的参数需要在目的地 Activity 的 onCreate 方法中 , 调用 getIntent().getXxxExtra() 获取到传递的值 ;

如果一次性传递 十几个 , 乃至几十个参数 , 这样就需要写很多次 getIntent().getXxxExtra() 样式的代码 , 这里引入注入框架 , 类似于 ButterKnife , 只要在目的 Activity 中的成员属性上标注注解 , 可以自动生成 getIntent().getXxxExtra() 相关逻辑 , 开发者不必手动编写此类逻辑 ;

ButterKnife 的作用是在 Activity 的成员属性上标注 @BindView 注解 , 自动生成 findViewById 代码 ;

二、自定义注解


自定义 Extra 注解 ,

@Target({ElementType.FIELD}) 元注解表示该注解用于标注成员字段 ,

@Retention(RetentionPolicy.CLASS) 元注解表示该注解保留到字节码编译时 ,

注解中定义了一个注解属性 name , 默认值为 “” ;

自定义注解代码示例 :

package kim.hsl.router_annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 参数自动注入注解* 该注解使用在 成员字段 上面*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Extra {/*** 参数名称* @return*/String name() default "";
}

三、使用 @Extra 自定义注解


在 Activity 中 , 使用 @Route 和 @Extra 自定义注解 ;

package kim.hsl.library3;import android.os.Bundle;import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;import android.view.View;import kim.hsl.router_annotation.Extra;
import kim.hsl.router_annotation.Route;@Route(path = "/library3/MainActivity")
public class MainActivity extends AppCompatActivity {/*** 姓名*/@Extraprivate String name;/*** 年龄*/@Extraprivate int age;/*** 身高*/@Extraprivate int height;/*** 体重*/@Extraprivate int weight;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Toolbar toolbar = findViewById(R.id.toolbar);}
}

四、注解处理器解析 @Extra 自定义注解 并生成相应 Activity 对应代码


注解处理器中解析上述注解 , 生成如下代码 , 生成代码位置 " D:\002_Project\002_Android_Learn\Component\library3\build\generated\ap_generated_sources\debug\out\kim\hsl\library3\MainActivity_Extra.java "

package kim.hsl.library3;import java.lang.Object;
import java.lang.Override;
import kim.hsl.route_core.template.IExtra;public class MainActivity_Extra implements IExtra {@Overridepublic void loadExtra(Object target) {MainActivity t = (MainActivity)target;t.name = t.getIntent().getStringExtra("name");t.age = t.getIntent().getIntExtra("age", t.age);t.height = t.getIntent().getIntExtra("height", t.height);t.weight = t.getIntent().getIntExtra("weight", t.weight);}
}

生成上述代码的注解处理器 :

package kim.hsl.router_compiler;import com.google.auto.service.AutoService;
import com.squareup.javapoet.ArrayTypeName;
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.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.WildcardTypeName;import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
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.TypeKind;
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.Extra;
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.Extra"})
// 支持的 Java 版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ExtraProcessor extends AbstractProcessor {/*** 注解处理器中使用 Messager 对象打印日志*/private Messager mMessager;/*** 用于写出生成的 Java 代码*/private Filer mFiler;/*** 注解节点工具*/private Elements mElementUtils;/*** 类工具*/private Types mTypeUtils;/*** 获取的 moduleName 参数*/private String mModuleName;/*** 获取所有需要注入的节点集合 , 并按照其父节点 Activity 进行分组* 键 ( Key ) : Activity 节点* 值 ( Value ) : Activity 中被 @Extra 注解的属性节点*/private Map<TypeElement, List<Element>> mActivity2Field = new HashMap<>();/*** 该函数在初始化时调用 , 相当于构造函数* @param processingEnvironment*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);// 获取打印日志接口this.mMessager = processingEnvironment.getMessager();// 测试日志打印mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 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, "ExtraProcessor : 打印 moduleName 参数 : " + mModuleName);}}/*** 该函数在注解处理器注册时自动执行, 是处理注解的核心函数** Set<? extends TypeElement> set 参数 : 该集合表示使用了相关注解的节点的集合** @param set* @param roundEnvironment* @return*/@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " process ");if (set == null || set.isEmpty()){// 如果没有检测到注解 , 直接退出mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 检测到注解为空 , 直接退出 mModuleName : " + mModuleName);return false;}// 获取被 @Extra 注解的属性节点集合Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Extra.class);// 采集这些属性节点集合的 类型 和 变量名称for (Element element : elements) {// 获取这些被 @Extra 标注的字段的父节点 Activity 节点TypeElement activityElement = (TypeElement) element.getEnclosingElement();if (mActivity2Field.containsKey(activityElement)) {// 如果该 Activity 父节点存在 , 直接添加到子节点集合中mActivity2Field.get(activityElement).add(element);} else {// 如果该 Activity 父节点不存在 , 先创建子节点集合 , 再添加到集合中List<Element> childs = new ArrayList<>();childs.add(element);mActivity2Field.put(activityElement, childs);}mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " 添加注解类型 : " + element.getSimpleName());}// 至此 , 已经将所有 Activity 以及其下使用 @Extra 标注的属性都存放在了// Map<TypeElement, List<Element>> mActivity2Field 集合中/*生成 Java 代码*/// 获取 Activity 类型TypeMirror activityTypeMirror = mElementUtils.getTypeElement("android.app.Activity").asType();// 获取 IExtra 接口类型节点TypeElement IExtra = mElementUtils.getTypeElement("kim.hsl.route_core.template.IExtra");// 生成 IExtra 接口中 void loadExtra(Object target); 方法的 Object target 参数ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();// 遍历所有需要注入的 类:属性for (Map.Entry<TypeElement, List<Element>> entry : mActivity2Field.entrySet()) {// 每个 Map 键值对元素都要生成一个对应的 Java 类// 获取 Activity 类TypeElement rawClassElement = entry.getKey();// 如果该类不是 Activity 子类 , 直接抛出异常if (!mTypeUtils.isSubtype(rawClassElement.asType(), activityTypeMirror)) {throw new RuntimeException("ExtraProcessor Activity 类型错误");}// 创建 void loadExtra(Object target) 方法MethodSpec.Builder builder = MethodSpec.methodBuilder("loadExtra").addAnnotation(Override.class).addModifiers(Modifier.PUBLIC).addParameter(objectParamSpec);// 生成类型转换代码 : MainActivity t = (MainActivity)target;// 获取 Activity 类名称ClassName className = ClassName.get(rawClassElement);// 类型转换, 将 Activity 类转为指定的 Activity 子类类型builder.addStatement("$T t = ($T)target", className, className);mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 开始循环 Map 元素个数" + entry.getValue().size());// 遍历被 @Extra 标注的属性字段for (int i = 0; i < entry.getValue().size(); i++) {Element element = entry.getValue().get(i);buildStatement(element, builder);}mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 结束循环 Map 元素个数" + entry.getValue().size());// 生成 java 类名, 原来的 Activity 类名基础上添加 "_Extra" 后缀String extraClassName = rawClassElement.getSimpleName() + "_Extra";// 创建 Java 类TypeSpec typeSpec = TypeSpec.classBuilder(extraClassName).addSuperinterface(ClassName.get(IExtra))   // 实现 IExtra 接口.addModifiers(PUBLIC)   //.addMethod(builder.build()) // 设置函数.build();   // 正式创建// Java 文件JavaFile javaFile = JavaFile.builder(className.packageName(), typeSpec).build();// 写出生成的 java 代码try {javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();}mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : 生成文件结束 : " + mModuleName + " " +javaFile.toString());}return true;}/*** 拼装如下代码* t.a = t.getIntent().getStringExtra("a");* @param element*/public void buildStatement(Element element, MethodSpec.Builder builder) {TypeMirror typeMirror = element.asType();int type = typeMirror.getKind().ordinal();//属性名 String text 获得textString fieldName = element.getSimpleName().toString();//获得注解 name值 , 默认是传入的 name 注解属性值String extraName = element.getAnnotation(Extra.class).name();mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : " + mModuleName + " 处理注解类型 : " + typeMirror.toString() + " , 字段名称 : " + fieldName + " , 注解属性值 : " + extraName);if (extraName == null || extraName.length() == 0) {// 如果 name 注解属性值为空 , 则取值 字段名称extraName = fieldName;}mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : extraName : " + extraName);String defaultValue = "t." + fieldName;String statement = defaultValue + " = t.getIntent().";if (type == TypeKind.BOOLEAN.ordinal()) {statement += "getBooleanExtra($S, " + defaultValue + ")";} else if (type == TypeKind.BYTE.ordinal()) {statement += "getByteExtra($S, " + defaultValue + ")";} else if (type == TypeKind.SHORT.ordinal()) {statement += "getShortExtra($S, " + defaultValue + ")";} else if (type == TypeKind.INT.ordinal()) {statement += "getIntExtra($S, " + defaultValue + ")";} else if (type == TypeKind.LONG.ordinal()) {statement += "getLongExtra($S, " + defaultValue + ")";} else if (type == TypeKind.CHAR.ordinal()) {statement += "getCharExtra($S, " + defaultValue + ")";} else if (type == TypeKind.FLOAT.ordinal()) {statement += "getFloatExtra($S, " + defaultValue + ")";} else if (type == TypeKind.DOUBLE.ordinal()) {statement += "getDoubleExtra($S, " + defaultValue + ")";} else{//数组类型if (type == TypeKind.ARRAY.ordinal()) {addArrayStatement(statement, fieldName, extraName, typeMirror, element, builder);} else {// 对象类型addObjectStatement(statement, fieldName, extraName, typeMirror, element, builder);}return;}builder.addStatement(statement, extraName);mMessager.printMessage(Diagnostic.Kind.NOTE, "ExtraProcessor : extraName : " + extraName + " 生成完毕");}private void addArrayStatement(String statement, String fieldName, String extraName, TypeMirrortypeMirror, Element elementm , MethodSpec.Builder builder) {// 获取 Parcelable 类型TypeMirror parcelableType = mElementUtils.getTypeElement("android.os.Parcelable").asType();// 处理数组switch (typeMirror.toString()) {case "boolean[]":statement += "getBooleanArrayExtra($S)";break;case "int[]":statement += "getIntArrayExtra($S)";break;case "short[]":statement += "getShortArrayExtra($S)";break;case "float[]":statement += "getFloatArrayExtra($S)";break;case "double[]":statement += "getDoubleArrayExtra($S)";break;case "byte[]":statement += "getByteArrayExtra($S)";break;case "char[]":statement += "getCharArrayExtra($S)";break;case "long[]":statement += "getLongArrayExtra($S)";break;case "java.lang.String[]":statement += "getStringArrayExtra($S)";break;default:// 处理 Parcelable 数组String defaultValue = "t." + fieldName;// object数组 componentType 获得 object类型ArrayTypeName arrayTypeName = (ArrayTypeName) ClassName.get(typeMirror);TypeElement typeElement = mElementUtils.getTypeElement(arrayTypeName.componentType.toString());// 如果不是 Parcelable 抛异常退出if (!mTypeUtils.isSubtype(typeElement.asType(), parcelableType)) {throw new RuntimeException("不支持的 Extra 类型 : " + typeMirror);}// 字符串格式statement = "$T[] " + fieldName + " = t.getIntent()" + ".getParcelableArrayExtra" + "($S)";builder.addStatement(statement, parcelableType, extraName);builder.beginControlFlow("if( null != $L)", fieldName);statement = defaultValue + " = new $T[" + fieldName + ".length]";builder.addStatement(statement, arrayTypeName.componentType).beginControlFlow("for (int i = 0; i < " + fieldName + "" +".length; " +"i++)").addStatement(defaultValue + "[i] = ($T)" + fieldName + "[i]",arrayTypeName.componentType).endControlFlow();builder.endControlFlow();return;}builder.addStatement(statement, extraName);}private void addObjectStatement(String statement, String fieldName, String extraName,TypeMirror typeMirror,Element element, MethodSpec.Builder builder) {// 获取 Parcelable 类型TypeMirror parcelableType = mElementUtils.getTypeElement("android.os.Parcelable").asType();// 获取 IService 类型TypeMirror iServiceType = mElementUtils.getTypeElement("kim.hsl.route_core.template.IService").asType();if (mTypeUtils.isSubtype(typeMirror, parcelableType)) {statement += "getParcelableExtra($S)";} else if (typeMirror.toString().equals("java.lang.String")) {statement += "getStringExtra($S)";} else if (mTypeUtils.isSubtype(typeMirror, iServiceType)) {ClassName routerClassName = ClassName.get("kim.hsl.route_core", "Router");statement = "t." + fieldName + " = ($T) $T.getInstance().build($S).navigation()";builder.addStatement(statement, TypeName.get(element.asType()), routerClassName,extraName);return;} else {// ListTypeName typeName = ClassName.get(typeMirror);//泛型if (typeName instanceof ParameterizedTypeName) {//list 或 arraylistClassName rawType = ((ParameterizedTypeName) typeName).rawType;//泛型类型List<TypeName> typeArguments = ((ParameterizedTypeName) typeName).typeArguments;if (!rawType.toString().equals("java.util.ArrayList") && !rawType.toString().equals("java.util.List")) {throw new RuntimeException("Not Support Inject Type:" + typeMirror + " " +element);}if (typeArguments.isEmpty() || typeArguments.size() != 1) {throw new RuntimeException("List Must Specify Generic Type:" + typeArguments);}TypeName typeArgumentName = typeArguments.get(0);TypeElement typeElement = mElementUtils.getTypeElement(typeArgumentName.toString());// Parcelable 类型if (mTypeUtils.isSubtype(typeElement.asType(), parcelableType)) {statement += "getParcelableArrayListExtra($S)";} else if (typeElement.asType().toString().equals("java.lang.String")) {statement += "getStringArrayListExtra($S)";} else if (typeElement.asType().toString().equals("java.lang.Integer")) {statement += "getIntegerArrayListExtra($S)";} else {throw new RuntimeException("Not Support Generic Type : " + typeMirror + " " +element);}} else {throw new RuntimeException("Not Support Extra Type : " + typeMirror + " " +element);}}builder.addStatement(statement, extraName);}}

五、博客资源


博客源码 :

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

【Android 组件化】路由组件 ( 页面跳转参数依赖注入 )相关推荐

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

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

  2. vue 如何处理两个组件异步问题_Vue异步组件处理路由组件加载状态的解决方案...

    vue.js 组件 组件(Component)是 Vue.js 最强大的功能之一. 组件可以扩展 HTML 元素,封装可重用的代码. 在大型单页面应用中,处于对性能的考虑和首屏加载速度的要求,我们一般 ...

  3. 谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo

    谈谈我对前端组件化中"组件"的理解,顺带写个Vue与React的demo 前言 前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的 ...

  4. React - 一般组件 withRouter 的使用(让一般组件具备路由组件特有的API属性)

    React - 一般组件 withRouter 的使用(让一般组件具备路由组件特有的API属性) 一. withRouter 理解 二. withRouter 使用 一. withRouter 理解 ...

  5. js带参数跳转php,JS实现页面跳转参数不丢失的方法

    本文实例讲述了JS实现页面跳转参数不丢失的方法.分享给大家供大家参考,具体如下: 需求:页面编辑后,返回列表页面,参数不丢失,能够记住页数以及筛选条件. 我坚信,不管白猫黑猫,能捉到耗子的就是好猫,当 ...

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

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

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

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

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

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

  9. android数据回传多个页面_Android Day06四大组件之Activity多页面跳转和数据传递

    ToolBar中menu无法同时显示图标和文字问题的解决方法 Toolbar添加返回按钮 ToolBar与ActionBar 1.什么是Activity 官方文档是这么说的:Anwhich users ...

最新文章

  1. 【ACM】LightOJ - 1010 Knights in Chessboard(不是搜索...)
  2. ADAS实际已涵盖20多种功能
  3. 【星球知识卡片】视频分类与行为识别有哪些核心技术,对其进行长期深入学习...
  4. linux unset path,linux查看和修改PATH环境变量的方法
  5. html5客户端本地存储之sessionStorage及storage事件
  6. Linux下Anaconda3安装及使用教程
  7. [C++11]弱引用智能指针weak_ptr初始化和相关的操作函数
  8. 春天重试,因为冬天来了
  9. 信息学奥赛一本通 1020:打印ASCII码 | OpenJudge NOI 1.2 07
  10. HQL - Hibernate查询语言 - 示例教程
  11. design短语的用法总结_中考英语常考的重点句型及短语汇总,考前必备
  12. 第二周四则运算汇报及总结
  13. 【Python脚本进阶】2.4、conficker蠕虫(下):暴破口令,远程执行进程
  14. spring-boot-starter-data-jpa详细使用介绍
  15. BTA分论坛现场直击|区块链+时下新科技,你了解多少?
  16. 全国城镇地理数据拼音、文字对照 JSON
  17. 《Python编程快速上手——让繁琐的工作自动化》读书笔记2
  18. Android 复杂的手势处理利用GestureOverView
  19. 【20】ubuntu下没有dos2unix命令怎么办
  20. Oracle APEX 系列文章2:在阿里云上打造属于你自己的APEX完整开发环境 (准备工作)

热门文章

  1. 陶哲轩实分析习题 12.1.3
  2. Microsoft Visual Studio 2010 Service Pack 1
  3. Linux下grep显示前后几行信息
  4. JVM调优总结(五)-分代垃圾回收详述1
  5. Eclipse插件使用links目录的用法
  6. Maven内置变量说明
  7. 统计输入的汉字,数字,英文,other数量
  8. 2016/01/13开始学习git:分支管理:Bug分支
  9. C++ 复制vector值到array,复制 array 到jintArray
  10. 第二篇:白话tornado源码之待请求阶段