首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android组件化专题-路由动态注入跳转参数以及获取其他模块的fragment

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

作者头像
用户3045442
发布2018-09-11 16:22:47
1.9K0
发布2018-09-11 16:22:47
举报
文章被收录于专栏:Android研究院Android研究院

微信公众号:Android研究院 关注可了解更多的Android知识,专注于移动领域,不知代码还有人生的哲学。 问题或建议,请公众号留言; 如果你觉得文章对你有帮助,欢迎赞赏[1]

上几篇组件化专题文章,我们对路由框架基本实现了,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;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        typeUtils = processingEnvironment.getTypeUtils();
        filer = processingEnvironment.getFiler();
        messager = processingEnvironment.getMessager();
        log = Log.newLog(messager);
    }

    @Override
    public 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和Fragment
        activity = 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)target
            builder.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 {
                    //Object
                    addObjectStatement(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) {
        //Parcelable
        if (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 {
            //List
            TypeName typeName = ClassName.get(typeMirror);
            //泛型
            if (typeName instanceof ParameterizedTypeName) {
                //list 或 arraylist
                ClassName 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, TypeMirror
            typeMirror, 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 {
  @Override
  public 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;

    @Override
    protected 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();
                }

实现效果如下:

image.png

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

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

APT实战

路由框架原理

模块间的业务通信

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android研究院 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 路由动态注入跳转参数
  • 实现fragment的跳转
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档