上几篇组件化专题文章,我们对路由框架基本实现了,Activity之间当跳转和模块间当通信。

现在思考一个问题: 不管是在Activity或者Fragment,跳转都会传递一些参数,然后在对应对Activity或着Fragment获取对应对参数,都要写大量重复的代码,然而我们作为一名有素质有梦想的码农,我们是不可以做这种搬砖的操作的,不想当大牛当码农不是好码农。

那么我们如何用优雅的代码,解决这个问题呢?请看下面分解

路由动态注入跳转参数

原生的获取intent跳转传递过来的参数:

Intent intent = getIntent();
final String path = intent.getStringExtra("path");
......
复制代码

同时还有fragment的参数传递等。

如果稍微进行一些封装的话可以这样写:

public class MainActivityExtra {public static void loadExtra(Main2Activity main2Activity) {Intent intent = main2Activity.getIntent();main2Activity.path = intent.getStringExtra("path");......}
}复制代码

上述不管哪种写法,都是相当麻烦和大量的重复代码。

最为一名有梦想的码农,我理想中的写法:

在相应的Activity,通过一个注解就可以拿到跳转传递过来的参数的值,然后直接使用。

@Extra
public String path;
复制代码

那么我们如何实现呢? 其实很简单,我们通过注解拿到父类Activity,然后注解变量的类型和名称,然后我们动态生成一个类,通过原生的方式来实现参数获取。

其实跟上几篇的文章是一样的原理,这里就不再多复述了这是链接组件化专题

下面就直接一堆代码,抛给你

  1. 首先声明注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface Extra {String name() default "";
}
复制代码
  1. 注解处理器处理注解,这里面的逻辑其实非常简单,主要是看你对APT使用是不是熟练
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({Consts.EXTRA_NAME})
@SupportedOptions({Consts.ARGUMENTS_NAME})
public class ExtraProcessor extends AbstractProcessor {/*** key:类节点 value:需要注入的属性节点集合*/private Map<TypeElement, List<Element>> extraMap = new HashMap<>();private Elements elementUtils;private Types typeUtils;private Filer filer;private Log log;private Messager messager;@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment) {super.init(processingEnvironment);elementUtils = processingEnvironment.getElementUtils();typeUtils = processingEnvironment.getTypeUtils();filer = processingEnvironment.getFiler();messager = processingEnvironment.getMessager();log = Log.newLog(messager);}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {if (null != set && !set.isEmpty()) {//注意这里必须要判断set.isEmpty()  否则会走两遍log.i("process");//拿到注有Extra 的节点集合Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(Extra.class);if (null != elementsAnnotatedWith) {try {saveExtras(elementsAnnotatedWith);generateExtras();} catch (Exception e) {e.printStackTrace();}}return true;}return false;}/*** 保存节点的信息** @param elementsAnnotatedWith*/private void saveExtras(Set<? extends Element> elementsAnnotatedWith) {for (Element element : elementsAnnotatedWith) {//获取注解父类的节点类型TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();if (extraMap.containsKey(enclosingElement)) {extraMap.get(enclosingElement).add(element);} else {List<Element> list = new ArrayList<>();list.add(element);extraMap.put(enclosingElement, list);}}}private MethodSpec.Builder builder;private TypeMirror parcelableType;private TypeMirror iServiceType;private TypeElement extraElement;private TypeMirror activity;/*** 生成表*/private void generateExtras() {//支持Activity和Fragmentactivity = elementUtils.getTypeElement(Consts.Activity).asType();extraElement = elementUtils.getTypeElement(Consts.Extra);parcelableType = elementUtils.getTypeElement(Consts.PARCELABLE).asType();iServiceType = elementUtils.getTypeElement(Consts.Service).asType();log.i("generateExtras");//参数ParameterSpec target = ParameterSpec.builder(TypeName.OBJECT, "target").build();if (!Utils.isEmpty(extraMap)) {//遍历被Extra注解属性的类for (Map.Entry<TypeElement, List<Element>> entry : extraMap.entrySet()) {TypeElement classType = entry.getKey();log.i("isSubtype:" + typeUtils.isSubtype(classType.asType(), activity) + " | " + classType.asType());if (typeUtils.isSubtype(classType.asType(), activity)) {generateClass(target, entry, classType);} else {throw new RuntimeException("[Just Support Activity Field] : " + classType + " --> " + typeUtils.isSubtype(classType.asType(), activity));}}}}public void generateClass(ParameterSpec target, Map.Entry<TypeElement, List<Element>> entry, TypeElement classType) {builder = MethodSpec.methodBuilder(Consts.EXTRA_METHOD_NAME)//方法名.addAnnotation(Override.class)//注解.addModifiers(Modifier.PUBLIC)//作用域.addParameter(target);//添加参数log.i("generate method");//设置函数体addFuncationBody(entry, classType);log.i("generate type");//生成Java类TypeSpec typeSpec = TypeSpec.classBuilder(classType.getSimpleName() + "$$Extra").addSuperinterface(ClassName.get(extraElement)).addModifiers(Modifier.PUBLIC).addMethod(builder.build()).build();try {//生成Java类文件log.i("package name:" + ClassName.get(classType).packageName());JavaFile.builder(ClassName.get(classType).packageName(), typeSpec).build().writeTo(filer);} catch (IOException e) {e.printStackTrace();log.i("package name error");}}private void addFuncationBody(Map.Entry<TypeElement, List<Element>> entry, TypeElement classType) {for (Element element : entry.getValue()) {//$T t = ($T)targetbuilder.addStatement("$T t = ($T)target", classType, classType);//遍历注解节点 生成函数体TypeMirror typeMirror = element.asType();//获取注解节点的类型//获取TypeKind 枚举类型的序列号int type = typeMirror.getKind().ordinal();//获取属性名String fieldName = element.getSimpleName().toString();//获取注解的值String extraName = element.getAnnotation(Extra.class).name();//判断直接的值为空的情况下的处理extraName = Utils.isEmpty(extraName) ? fieldName : 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);} else {//ObjectaddObjectStatement(statement, fieldName, extraName, typeMirror, element);}return;}log.i("generate statement:" + statement);builder.addStatement(statement, extraName);}}/*** 添加对象 String/List/Parcelable** @param statement* @param extraName* @param typeMirror* @param element*/private void addObjectStatement(String statement, String fieldName, String extraName,TypeMirror typeMirror,Element element) {//Parcelableif (typeUtils.isSubtype(typeMirror, parcelableType)) {statement += "getParcelableExtra($S)";} else if (typeMirror.toString().equals(Consts.STRING)) {statement += "getStringExtra($S)";} else if (typeUtils.isSubtype(typeMirror, iServiceType)) {
//            TestService testService = (TestService) DNRouter.getInstance().build("/main/service1")
//                    .navigation();
//            testService.test();statement = "t." + fieldName + " = ($T) $T.getInstance().build($S).navigation()";builder.addStatement(statement, TypeName.get(element.asType()), Consts.ROUTER,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(Consts.ARRAYLIST) && !rawType.toString().equals(Consts.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 = elementUtils.getTypeElement(typeArgumentName.toString());// Parcelable 类型if (typeUtils.isSubtype(typeElement.asType(), parcelableType)) {statement += "getParcelableArrayListExtra($S)";} else if (typeElement.asType().toString().equals(Consts.STRING)) {statement += "getStringArrayListExtra($S)";} else if (typeElement.asType().toString().equals(Consts.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);}/*** 添加数组** @param statement* @param fieldName* @param typeMirror* @param element*/private void addArrayStatement(String statement, String fieldName, String extraName, TypeMirrortypeMirror, Element element) {//数组switch (typeMirror.toString()) {case Consts.BOOLEANARRAY:statement += "getBooleanArrayExtra($S)";break;case Consts.INTARRAY:statement += "getIntArrayExtra($S)";break;case Consts.SHORTARRAY:statement += "getShortArrayExtra($S)";break;case Consts.FLOATARRAY:statement += "getFloatArrayExtra($S)";break;case Consts.DOUBLEARRAY:statement += "getDoubleArrayExtra($S)";break;case Consts.BYTEARRAY:statement += "getByteArrayExtra($S)";break;case Consts.CHARARRAY:statement += "getCharArrayExtra($S)";break;case Consts.LONGARRAY:statement += "getLongArrayExtra($S)";break;case Consts.STRINGARRAY:statement += "getStringArrayExtra($S)";break;default://Parcelable 数组String defaultValue = "t." + fieldName;//object数组 componentType获得object类型ArrayTypeName arrayTypeName = (ArrayTypeName) ClassName.get(typeMirror);TypeElement typeElement = elementUtils.getTypeElement(arrayTypeName.componentType.toString());//是否为 Parcelable 类型if (!typeUtils.isSubtype(typeElement.asType(), parcelableType)) {throw new RuntimeException("Not Support Extra Type:" + typeMirror + " " +element);}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);}
}复制代码

其实上面的注解处理器,会动态生成这样的一个类

public class Main2Activity$$Extra implements IExtra {@Overridepublic void loadExtra(Object target) {Main2Activity t = (Main2Activity)target;t.path = t.getIntent().getStringExtra("path");}
}
复制代码
  1. 通过一个API去加载这个类
 public void loadExtra(Activity activity) {String name = activity.getClass().getName();IExtra iExtra = extraLruCache.get(name);try {if (iExtra == null) {iExtra = (IExtra) Class.forName(activity.getClass().getName() + "$$Extra").getConstructor().newInstance();}iExtra.loadExtra(activity);extraLruCache.put(name, iExtra);} catch (Exception e) {e.printStackTrace();}}
复制代码
  1. 具体的调用实现
public class ModuleTest2Activity extends AppCompatActivity {@Extra(name = "path")public String url;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_module_test2);PrimRouter.getInstance().inject(this);TextView module2_textView = findViewById(R.id.module2_textView);
//        Intent intent = getIntent();
//        final String path = intent.getStringExtra("path");module2_textView.setText("我是module2,我的地址是:/module2/test. 我是被地址:" + url + " 调起的");}
}
复制代码

这样处理不仅大大提高了编码效率还减少了大量重复的代码,也提高了程序的可读性.

实现fragment的跳转

这里我们需要在原来的基础上,加几句代码。代码地址

//拿到fragment的全类名
TypeElement fragment = elementUtils.getTypeElement(Consts.Fragment);TypeElement v4Fragment = elementUtils.getTypeElement(Consts.V4Fragment);//单个的节点for (Element element : annotatedWith) {// 获取类信息 如Activity类TypeMirror typeMirror = element.asType();// 获取节点的注解信息Router annotation = element.getAnnotation(Router.class);log.i(typeMirror + " | " + activity.asType());//只能指定的类上面使用if (typeUtils.isSubtype(typeMirror, activity.asType())) {//存储路由相关的信息routerMeta = new RouterMeta(RouterMeta.Type.ACTIVITY, annotation, element);} else if (typeUtils.isSubtype(typeMirror, service.asType())) {//存储路由相关的信息routerMeta = new RouterMeta(RouterMeta.Type.SERVICE, annotation, element);} else if (typeUtils.isSubtype(typeMirror, fragment.asType()) || typeUtils.isSubtype(typeMirror, v4Fragment.asType())) {//存储路由相关的信息routerMeta = new RouterMeta(RouterMeta.Type.FRAGMENT, annotation, element);} else {throw new RuntimeException("Just Support Activity Router!");}//检查是否配置group如果没有配置 则从path中截取组名checkRouterGroup(routerMeta);}
复制代码

然后在我们的API库,返回fragment

case FRAGMENT:Class<?> fragment = jumpCard.getDestination();try {Object instance = fragment.getConstructor().newInstance();if (instance instanceof Fragment) {((Fragment) instance).setArguments(jumpCard.getExtras());} else if (instance instanceof android.support.v4.app.Fragment) {((android.support.v4.app.Fragment) instance).setArguments(jumpCard.getExtras());}return instance;} catch (Exception e) {e.printStackTrace();}
复制代码

实现效果如下:

到此为止,我们的路由框架实现了以下功能: 支持直接解析标准URL路由地址进行跳转,并自动注入参数到目标页面中 支持多模块工程使用 支持模块间的通信 支持获取其他模块的fragment

Android的组件化专题: 组件化配置

APT实战

路由框架原理

模块间的业务通信

Android组件化专题-路由动态注入跳转参数以及获取其他模块的fragment相关推荐

  1. Android组件化专题 - 路由框架原理

    在路由框架之前,我们先了解什么是APT,并实践ButterKnife绑定findById的小功能.为什么先要讲解apt,因为路由的实现apt是核心的代码.看下面链接 APT 实践. 本文项目地址 为什 ...

  2. 【Android 组件化】路由组件 ( 页面跳转参数依赖注入 )

    文章目录 一.参数自动注入 二.自定义注解 三.使用 @Extra 自定义注解 四.注解处理器解析 @Extra 自定义注解 并生成相应 Activity 对应代码 五.博客资源 组件化系列博客 : ...

  3. Android组件化专题 - 组件化配置

    demo地址 Android组件化专题,详细讲解组件化的使用及配置,以及实现的原理. 本文章讲解了组件化的由来及配置,下期讲解页面路由跳转及路由原理与apt 1. 组件化的由来 模块化.组件化和插件化 ...

  4. 【Android 组件化】路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )

    文章目录 一.注解节点类型 二.JavaPoet 简介 三.注解处理器中使用 JavaPoet 生成代码 四.路由框架说明 五.博客资源 组件化系列博客 : [Android 组件化]从模块化到组件化 ...

  5. 【Android 组件化】路由组件 ( 路由框架概述 )

    文章目录 一.路由框架概述 二.路由框架整体流程 三.博客资源 组件化系列博客 : [Android 组件化]从模块化到组件化 [Android 组件化]使用 Gradle 实现组件化 ( Gradl ...

  6. 【Android 组件化】路由组件 ( 生成 Root 类记录模块中的路由表 )

    文章目录 一.Root 表作用 二.生成 Root 表 三.完整注解处理器代码 及 生成的 Java 代码 ( 仅供参考 ) 1.注解处理器代码 2.app 模块中的注解类生成的 Java 源码 3. ...

  7. 【Android 组件化】路由组件 ( 组件间共享的服务 )

    文章目录 一.组件间共享的服务 二.注解处理器添加对上述 " 组件间共享的服务 " 的支持 三.注解处理器 生成代码规则 四.完整注解处理器代码 及 生成的 Java 代码 1.注 ...

  8. 【Android 组件化】路由组件 ( 注解处理器调试 )

    文章目录 一.添加断点 二.获取调试选项 三.创建 Gradle 调试任务 四.开始调试 五.博客资源 组件化系列博客 : [Android 组件化]从模块化到组件化 [Android 组件化]使用 ...

  9. 【Android 组件化】路由组件 ( 使用 JavaPoet 生成路由表类 )

    文章目录 一.要生成的路由表类 二.生成 路由表 过程 1.获取其它类节点 2.生成参数 3.路由表结构 4.函数创建 5.Java 类创建 6.写出 Java 源码到文件中 三.完整注解处理器及运行 ...

最新文章

  1. python导出json
  2. 网站开发绝不像你看到的那么简单!
  3. java——原型模式
  4. 搜索功能:洞悉产品的绝佳入口
  5. 本地maven仓库_Maven(四):仓库
  6. 到底什么是面向对象,面试中怎么回答。面向过程和面向对象的区别是什么。java跨平台特性以及java和C++的区别。面向对象的三大特性——封装、继承和多态。面向对象的高拓展性以及低耦合度怎么体现?
  7. 闲谈简单设计(KISS)疑惑
  8. java log 断点_项目中常见的log日志调用
  9. java restful中文乱码_restful服务接口访问乱码 和 505错误
  10. NeurIPS | 谷歌使用机器学习如何做好分布外异常检测
  11. (102)FPGA面试题-如何选择FPGA型号?
  12. TIOBE 6 月编程语言排行榜:Python 势不可挡,或在四年之内超越 Java、C
  13. JDK 8_jstack命令使用
  14. Android中文API(98)—— ContextMenu.ContextMenuInfo
  15. 10分钟就能搭建远程开发环境?你早点怎么不出现(#`n´)!
  16. 一分钟了解微服务的好处和陷阱
  17. linux飞信机器人,在Nagios使用飞信机器人发送警报
  18. 使用yq工具合并两个yml文件
  19. Win10教育版变为企业版
  20. 计算机图形学画简单图形,计算机图形学 基本图形绘制 Koch雪花绘制

热门文章

  1. linux与windows双系统保持时间同步
  2. 传统公司部署OpenStack(t版)简易介绍(八)——Dashboard模块部署
  3. ps自定义形状工具_【福利】3000款PS自定义形状免费打包下载
  4. red hat linux5 u盘安装,RHEL5安装,Red Hat Enterprise Linux 5安装文档
  5. python 批量创建线程_【Python】批量创建线程
  6. 如果计算机用户有密码 待机,电脑待机密码怎么设置
  7. java线程分类_Java 线程类别
  8. 计算机科学 在职双证,计算机专业在职研究生如何获得双证?
  9. pandas数据处理分组聚合
  10. python数字倒序