组件化

Q:组件化与模块化区别?

  • 组件化:组件化,去除模块间的耦合,使得每个业务模块可以独立当做App存在,对于其他模块没有直接的依赖关系
  • 模块化:模块化就是拆分成多个模块放在不同的Module里面,每个功能的代码都在自己所属的 module 中添加

Q:业务组件,如何实现单独运行调试?

  • 单工程方案,组件以module形式存在,动态配置组件的工程类型;
    • 动态修改gradle文件,集成时变为Library插件,独立调试时变为Application插件
    • 动态配置ApplicationId 和 AndroidManifest
  • 多工程方案,业务组件以library module形式存在于独立的工程,且只有这一个library module。
    • 业务组件以library module形式存在于独立的工程
    • 发布到Maven仓库,通过引入第三方依赖方式使用

Q:业务组件间 没有依赖,如何实现页面的跳转?

使用路由

Q:业务组件间 没有依赖,如何实现组件间通信/方法调用?

  • 服务暴露组件

Q:业务组件不能反向依赖壳工程,如何获取Application实例、如何获取Application onCreate()回调(用于任务初始化)?

通过AppLifeCycle回调

Q:如何实现一个路由框架?

  1. 不使用注解器
    • 手动把每个界面和相应的Activity对应起来。这个处理过程可以在Application里面做的
    routers.put("router://activity/main", MainActivity.class);
    routers.put("router://activity/main2", Main2Activity.class);
    routers.put("router://activity/main3", Main3Activity.class);
    //通过scheme实现跳转
    Class aClass = mTables.get(path);
    Intent intent = new Intent(context, aClass);
    if (!(context instanceof Activity)) {
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    }
    context.startActivity(intent);
    
  2. 使用注解器
    • 自动生成用于注册activity的map代码
      • 2.1、 继承AbstractProcessor 每一个处理器都继承与AbstractProcessor,这些方法java虚拟机会自动调用
          public class MyProcessor extends AbstractProcessor {
          @Override
          public synchronized void init(ProcessingEnvironment env){ }
          @Override
          public boolean process(Set<? extends TypeElement> annoations, RoundEnvironment env) { 
              //处理函数,我们需要在这个函数里面写处理特定注解的代码,并生成相应的java文件,RoundEnvironment,通过这个参数我们可以拿到带有特定注解的元素(类,字段,方法啦)
          }
          @Override
          public Set<String> getSupportedAnnotationTypes() { 
              //指定当前的注解处理器处理哪些注解,从返回值可以看到是返回Set的,那就是一个注解处理器可以处理多个注解的
          }
          @Override
          public SourceVersion getSupportedSourceVersion() {
              //指定你使用的java版本,通常这里直接返回SourceVersion.latestSupported()
           }
      }
      
      • 2.2、注册处理器 需要有特定的文件在src/main/resources/META-INF/services中,文件名是javax.annotation.processing.Processor,内容是处理器的路径
      • 2.3、处理器的编写
        • 注解存放库 只存放注解
          @Target(ElementType.TYPE)
          @Retention(RetentionPolicy.CLASS)
          public @interface Route {
              String value();
          }
          
        • 注解解析库 处理注解,并不会增加apk的大小
          public class RouterProcessor extends AbstractProcessor {
              @Override
              public synchronized void init(ProcessingEnvironment processingEnv) {
                  super.init(processingEnv);
                  UtilManager.getMgr().init(processingEnv);
                  //ProcessingEnvironment中的内容
                  //package com.example;    // PackageElement
                  //public class Foo {        // TypeElement
                  //    private int a;      // VariableElement
                  //    private Foo other;  // VariableElement
                  //    public Foo () {}    // ExecuteableElement
                  //    public void setA (  // ExecuteableElement
                  //        int newA   // TypeElement
                  //    ) {}
                  //}
              }
              @Override
              public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
                  UtilManager.getMgr().getMessager().printMessage(Diagnostic.Kind.NOTE, "process");
                  //获取所有被Route注解的Element
                  Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
                  List<TargetInfo> targetInfos = new ArrayList<>();
                  for (Element element : elements) {
                      // 检查类型
                      if (!Utils.checkTypeValid(element)) continue;
                      //获取一个被Route注解的类,并添加到list中
                      TypeElement typeElement = (TypeElement) element;
                      Route route = typeElement.getAnnotation(Route.class);
                      targetInfos.add(new TargetInfo(typeElement, route.value()));
                  }
                  if (!targetInfos.isEmpty()) {
                      generateCode(targetInfos);
                  }
                  return false;
              }
              /**
              * 生成对应的java文件
              *
              * @param targetInfos 代表router和activity
              */
              private void generateCode(List<TargetInfo> targetInfos) {
                  // Map<String, Class<? extends Activity>> routers
                  TypeElement activityType = UtilManager
                      .getMgr()
                      .getElementUtils()
                      .getTypeElement("android.app.Activity");
          
                  ParameterizedTypeName actParam = ParameterizedTypeName.get(ClassName.get(Class.class),
                          WildcardTypeName.subtypeOf(ClassName.get(activityType)));
                  ParameterizedTypeName parma = ParameterizedTypeName.get(ClassName.get(Map.class),
                          ClassName.get(String.class), actParam);
                  ParameterSpec parameterSpec = ParameterSpec.builder(parma, "routers").build();
          
                  MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder(Constants.ROUTE_METHOD_NAME)
                          .addAnnotation(Override.class)
                          .addModifiers(Modifier.PUBLIC)
                          .addParameter(parameterSpec);
                  for (TargetInfo info : targetInfos) {
                      methodSpecBuilder.addStatement("routers.put($S, $T.class)", info.getRoute(), info.getTypeElement());
                  }
                  TypeElement interfaceType = UtilManager
                          .getMgr()
                          .getElementUtils()
                          .getTypeElement(Constants.ROUTE_INTERFACE_NAME);
                  TypeSpec typeSpec = TypeSpec.classBuilder(Constants.ROUTE_CLASS_NAME)
                          .addSuperinterface(ClassName.get(interfaceType))
                          .addModifiers(Modifier.PUBLIC)
                          .addMethod(methodSpecBuilder.build())
                          .addJavadoc("Generated by Router. Do not edit it!\n")
                          .build();
                  try {
                      JavaFile.builder(Constants.ROUTE_CLASS_PACKAGE, typeSpec)
                              .build()
                              .writeTo(UtilManager.getMgr().getFiler());
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          
              /**
              * 定义你的注解处理器注册到哪些注解上
              */
              @Override
              public Set<String> getSupportedAnnotationTypes() {
                  Set<String> annotations = new LinkedHashSet<>();
                  annotations.add(Route.class.getCanonicalName());
                  return annotations;
              }
              /**
              * java版本
              */
              @Override
              public SourceVersion getSupportedSourceVersion() {
                  return SourceVersion.latestSupported();
              }
          }
          
        • 对外API库
              public interface IRoute {
                  void initRouter(Map<String, Class<? extends Activity>> routers);
              }
              public void init() {
                  try {
                      //获取通过注解生成的文件,执行map的初始化
                      String className = "com.ai.router.impl.AppRouter";
                      Class<?> moduleRouteTable = Class.forName(className);
                      Constructor constructor = moduleRouteTable.getConstructor();
                      IRoute instance = (IRoute) constructor.newInstance();
                      instance.initRouter(mTables);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
              //实行跳转
              public void openResult(Context context, String path) {
                  if (!TextUtils.isEmpty(mSchemeprefix)) {
                      // router://activity/main
                      path = mSchemeprefix + "://" + path;
                  }
                  try {
                      Class aClass = mTables.get(path);
                      Intent intent = new Intent(context, aClass);
                      if (!(context instanceof Activity)) {
                          intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                      }
                      context.startActivity(intent);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }