组件化框架

  • 组件之间只能通过接口和 Uri 进行通讯(隔离代码)
  • 各个组件均可独立运行(可以执行 Assemble 任务)
  • 校验 + 冲突检测(接口实现、Uri 实现)

首先,各个组件都依赖 ComponentService 模块,该模块包含所有 Service 接口以及基于注解处理器生成的 Uri 信息

sourceSets {
    main {
        java.srcDirs = [
            'src/main/java',
            project.getRootDir().getAbsolutePath() + '/RouteTable/java'
        ]
    }
}

我们通过 isRunAlone 来设置组件是否可以独立运行,通过 debugComponent 和 releaseComponent 设置组件之间的依赖关系

//各个子工程gradle.properties:
isRunAlone=true
debugComponent=onecomponent,secondcomponent,thirdcomponent
compileComponent=onecomponent

//各个子工程build.gradle:
apply plugin: 'com.lede.component'
dependencies {
    api project(':componentservice')
    annotationProcessor project(':component-annotation')
}

//根工程gradle.properties:
mainmodulename=app

我们在 Gradle 插件、Android Transform、AnnotationProcessor 中处理具体逻辑。首先我们获取当前执行的 Task 信息,判断 Task 类型(Assemble、Release、Debug),并通过分割 taskNames 获取当前要执行的插件。如果当前是 Assemble 任务,我们需要动态将其他插件的 isRunAlone 修改为 false

//taskNames: project.gradle.startParameter.taskNames
private static AssembleTask getTaskInfo(List<String> taskNames) {
  AssembleTask assembleTask = new AssembleTask()
  for (String task : taskNames) {
    if (task.toUpperCase().contains("ASSEMBLE") || task.contains("aR")) {
      if (task.toUpperCase().contains("DEBUG")) {
        assembleTask.isDebug = true
      }
      assembleTask.isAssemble = true
      String[] strs = task.split(":")
      assembleTask.modules.add(strs.length > 1 ? strs[strs.length - 2] : "")
      break
    }
  }
  return assembleTask
}

private void fetchMainmodulename(Project project, AssembleTask assembleTask) {
  if (!project.rootProject.hasProperty("mainmodulename")) {
    throw new RuntimeException(
               "you should set mainmodulename in root project's gradle.properties")
  }
  if (assembleTask.modules.size() > 0 && assembleTask.modules.get(0) != null 
          && assembleTask.modules.get(0).trim().length() > 0
          && !assembleTask.modules.get(0).equals("")) {
    compileModule = assembleTask.modules.get(0)
  } else {
    compileModule = project.rootProject.property("mainmodulename")
  }
  if (compileModule == null || compileModule.trim().length() <= 0) {
    throw new RuntimeException(
               "you should set mainmodulename in root project's gradle.properties")
  }
}

//对于isRunAlone==true的情况需要根据实际情况修改其值
boolean isRunAlone = Boolean.parseBoolean((project.properties.get("isRunAlone")))
String mainmodulename = project.rootProject.property("mainmodulename")
if (isRunAlone && assembleTask.isAssemble) {
  //对于要编译的组件和主项目,isRunAlone修改为true,其他组件都强制修改为false
  //这就意味着组件不能引用主项目,这在层级结构里面也是这么规定的
  if (module.equals(compileModule) || module.equals(mainmodulename)) {
    isRunAlone = true
  } else {
    isRunAlone = false
  }
}

对于 RunAlone 模式的插件,为其添加 application 插件,添加 RunAlone 资源目录,添加组件依赖,并注册 Transform Api。对于非 RunAlone 模式的插件,为其添加 library 插件

if (isRunAlone) {
  project.apply plugin: 'com.android.application'
  if (!module.equals(mainmodulename)) {
    project.android.sourceSets {
      main {
        manifest.srcFile 'src/main/runalone/AndroidManifest.xml'
        java.srcDirs = ['src/main/java', 'src/main/runalone/java']
        res.srcDirs = ['src/main/res', 'src/main/runalone/res']
        jniLibs.srcDirs = ['libs', 'src/main/jniLibs', 'src/main/runalone/jniLibs']
      }
    }
  }
  if (assembleTask.isAssemble && module.equals(compileModule)) {
    compileComponents(assembleTask, project)
    project.android.registerTransform(new ComCodeTransform(project))
  }
} else {
  project.apply plugin: 'com.android.library'
}

private void compileComponents(AssembleTask assembleTask, Project project) {
  String components
  if (assembleTask.isDebug) {
    components = (String) project.properties.get("debugComponent")
  } else {
    components = (String) project.properties.get("compileComponent")
  }
  String[] compileComponents = components.split(",")
  for (String str : compileComponents) {
    Project targetProject = getTargetProject(project, str.trim())
    if (targetProject != null) {
      project.dependencies.add("implementation", targetProject)
    }
  }
}

private Project getTargetProject(Project project, String keywords) {
  Project targetProject = null
  project.rootProject.subprojects.each {
    String[] arrays = it.name.split(":")
    if (arrays[arrays.length - 1].toUpperCase().equals(keywords.toUpperCase())) {
      targetProject = it
    }
  }
  return targetProject
}

所有组件都要实现 IApplicationLike 接口,另外 RunaloneApplicaiton 也实现了 IApplicationLike 接口。在 Transform 中,我们收集实现 IApplicationLike 接口的类(排除继承 RunaloneApplicaiton 的类),并记录到 ComponentCollectors

private static boolean isActivator(CtClass ctClass) {
  for (CtClass ctClassInter : ctClass.getInterfaces()) {
    if (ctClassInter.name.endsWith("IApplicationLike")) return true
  }
  return false
}

//从activators中去掉application
Iterator<CtClass> iterator = activators.iterator()
while (iterator.hasNext()) {
   CtClass ctClass = iterator.next()
   if (!"java.lang.Object".equals(ctClass.getSuperclass().name)) {
      iterator.remove()
   }
}

/*
 * this is auto generate file
 * TradeApplike=com.lede.netease.trade.applike.TradeApplike
 * LoginApplike=com.lede.netease.login.applike.LoginApplike
 * ChartApplike=com.lede.netease.chart.applike.ChartApplike
 */

public class TradeApplike implements IApplicationLike {
  @Override
  public void initComponent(Context context) {

  }
  @Override
  public String getComponentName() {
    return "trade";
  }
}

组件之间通过 Router 类以及接口和短链进行通讯:

Router.getInstance().getService(TradeService.class);
Router.getInstance().openUri(getActivity(),"ntesfa://login/LoginActivity",null);

在 RunaloneApplicaiton 的 onCreate 方法中反射获取 ComponentCollectors 类,读取并通过反射实例化各个组件的入口类,调用 initComponent 方法,然后通过 getComponentName 方法获取组件名,进而反射实例化(组件名$$ComponentRouter)类,实例保存在 Router 中用来实现路由跳转

public class RunaloneApplication extends IApplicationLike {
  public void initComponent(Context context) {
    SequencedProperties prop = getComponentProperties();
    for (Object key : prop.getKeyList()) {
      Class clazz = Class.forName(prop.getProperty(key.toString()));
      IApplicationLike appLike = (IApplicationLike) clazz.newInstance();
      Router.getInstance().registerRouter(appLike.getComponentName());
      mIApplicationLikes.add(appLike);
    }
    for(IApplicationLike applike : mIApplicationLikes){
      applike.initComponent(context);
    }
  }

  public SequencedProperties getComponentProperties() {
    InputStream in = ComponentConfig.class.getResourceAsStream("/assets/component.properties");
    SequencedProperties prop = new SequencedProperties();
    prop.load(in);
    return prop;
  }
}

public class Router implements IRouter {
  @Override
  public void registerRouter(String componentName) {
    IComponentRouter router = getComponentRouter(componentName);
    if (router != null) {
      registerRouter(router);
    }
  }

  public void registerRouter(IComponentRouter router) {
    if (componentRouters.contains(router)){
      componentRouters.remove(router);
    }
    componentRouters.add(router);
  }

  private IComponentRouter getComponentRouter(String componentName) {
    String path = genComponentRouterClass(componentName);
    if (routerInstanceCache.containsKey(path)) {
      return routerInstanceCache.get(path);
    }
    Class cla = Class.forName(path);
    IComponentRouter instance = (IComponentRouter) cla.newInstance();
    routerInstanceCache.put(path, instance);
    return instance;
  }

  public static String genComponentRouterClass(String componentName) {
    return "com.lede.gen.router" + "." + firstCharUpperCase(componentName) + "$$ComponentRouter";
  }
}

在路由跳转的时候,Router 会遍历所有的 ComponentRouter,如果 Component 没有初始化,则先执行 initMap 方法

public class Login$$ComponentRouter extends ComponentRouter {
  @Override
  public String getComponentName() {
    return "login";
  }

  @Override
  public void initMap() {
    super.initMap();
    routeMapper.put("/login/thirdActivity",ThirdActivity.class);
    routeMapper.put("/login/secondActivity",SecondActivity.class);
    serviceMapper.put(SecondService.class,SecondServiceImpl.class);
  }
}

public class Router implements IRouter {
  // ntesfa://secondcomponent/secondActivity
  @Override
  public boolean openUri(Context context, String url, Bundle bundle) {
    Uri uri = Uri.parse(url);
    return openUri(context, uri, bundle);
  }

  @Override
  public boolean openUri(Context context, Uri uri, Bundle bundle) {
    for (IComponentRouter temp : componentRouters) {
      if(temp.verifyUri(uri)){
        temp.openUri(context, uri, bundle);
      }
    }
    return true;
  }
}

public abstract class ComponentRouter implements IComponentRouter {
  @Override
  public boolean verifyUri(Uri uri) {
    if (uri == null || uri.getScheme() == null) {
      return false;
    }
    String host = uri.getHost();
    if (!getComponentName().equals(host)) {
      return false;
    }
    if (!hasInitMap) {
      initMap();
    }
    return true;
  }
}

对于接口实现类,需要添加 @ServiceNode 注解,对于 Uri 实现类,需要添加 @RouteNode(path = "/login/thirdActivity") 注解,框架对应生成(组件名$$ComponentRouter)类

具体原理:javapoet、Annotation Processing、AutoService

Annotation Processor 是 Javac 的一个工具,它用来在编译时扫描和处理注解,自定义注解处理器需要继承 AbstractProcessor 类,并添加 @AutoService 注解,该注解可以自动生成 META-INF/services/javax.annotation.processing.Processor 文件,AutoService 是 Google 开发的一个库,使用时需要添加依赖

AbstractProcessor 是一个抽象类,有四个核心方法:init、process、getSupportedSourceVersion、getSupportedAnnotationTypes

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
  super.init(processingEnv);
  Filer mFiler = processingEnv.getFiler();
  Types mTypeUtils = processingEnv.getTypeUtils();
  Messager mMessager = processingEnv.getMessager();
  Elements mElementUtils = processingEnv.getElementUtils();
  Map<String, String> options = processingEnv.getOptions();
  componentName = options.get("moduleName");
}

TypeElement代表源代码中的元素类型(包、类、方法、变量),但是TypeElement并不包含类的相关信息,你可以从TypeElement获取类的名称,但你不能获取类的信息,比如说父类,这些信息可以通过TypeMirror获取。你可以通过调用element.asType()来获取TypeElementTypeMirror

//RouterProcessor.java

String ACTIVITY = "android.app.Activity";

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  Set<? extends Element> routeNodes = roundEnv.getElementsAnnotatedWith(RouteNode.class);
  if(routeNodes.size() > 0) {
    TypeMirror type_Activity = mElementUtils.getTypeElement(ACTIVITY).asType();
    for(Element element : routeNodes) {
      TypeMirror tm = element.asType();
      RouteNode routeNode = element.getAnnotation(RouteNode.class);
      if (processingEnv.getTypeUtils().isSubtype(tm, type_Activity)){
        Node node = new Node();
        node.setPath(routeNode.path());
        node.setDesc(routeNode.desc());
        node.setRawType(element);
        node.setNodeType(NodeType.TYPE_ACTIVITY); //NodeType.TYPE_SERVICE
        if (!nodesList.contains(node)) {
          nodesList.add(node);
        }
      }
    }
    generateComponentRouter();//生成组件componentRouter
    generateRouterTableJava();//生成组件路由表
  }
}

String COMPONENT_ROUTER = "com.lede.component.componentlib.router.ComponentRouter";

private void generateComponentRouter() {
  //com.lede.gen.router.ThirdComponentComponentRouter
  String claName = RouteUtils.genComponentRouterClass(componentName);
  //pkg
  String pkg = claName.substring(0, claName.lastIndexOf("."));
  //simpleName
  String cn = claName.substring(claName.lastIndexOf(".") + 1);
  //superClassName
  TypeElement typeBase = processingEnv.getElementUtils().getTypeElement(COMPONENT_ROUTER);
  /********* JavaPoet *********/
  ClassName superClass = ClassName.get(typeBase);
  MethodSpec getComponentNameMethod = generateGetComponentNameMethod();
  MethodSpec initMapMethod = generateInitMapMethod();
  JavaFile.builder(pkg, TypeSpec.classBuilder(cn)
    .addModifiers(PUBLIC)
    .superclass(superClass)
    .addMethod(getComponentNameMethod)
    .addMethod(initMapMethod)
    .build()
  ).build().writeTo(mFiler);
}

private MethodSpec generateGetComponentNameMethod() {
  TypeName returnType = TypeName.get(processingEnv.getElementUtils()
                                .getTypeElement("java.lang.String").asType());
  MethodSpec.Builder getComponentNameMethodSpecBuilder = MethodSpec.methodBuilder("getComponentName")
                                .returns(returnType)
                                .addAnnotation(Override.class)
                                .addModifiers(Modifier.PUBLIC);
  getComponentNameMethodSpecBuilder.addStatement("return $S",componentName);
  return getComponentNameMethodSpecBuilder.build();
}

//$S for Strings
//$T for Types
private MethodSpec generateInitMapMethod() {
  TypeName returnType = TypeName.VOID;
  MethodSpec.Builder initMapMethodSpecBuilder =
                                 MethodSpec.methodBuilder("initMap")
                                   .returns(returnType)
                                   .addAnnotation(Override.class)
                                   .addModifiers(Modifier.PUBLIC);
  initMapMethodSpecBuilder.addStatement("super.initMap()");
  for (Node node : nodesList) {
    if(node.getNodeType() == NodeType.TYPE_ACTIVITY) {
      initMapMethodSpecBuilder.addStatement(
        "routeMapper" + ".put($S,$T.class)",
        node.getPath(),
        ClassName.get((TypeElement) node.getRawType()));
    }
  }
  return initMapMethodSpecBuilder.build();
}
//InjectProcessor.java

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
  categories(roundEnv.getElementsAnnotatedWith(Inject.class));
  generateHelper();
  return true;
}

private void categories(Set<? extends Element> elements) {
  for (Element element : elements) {
    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
    if (element.getModifiers().contains(Modifier.PRIVATE)) {
      throw new IllegalAccessException("The injected fields CAN NOT BE private");
    }
    if (parentAndChild.containsKey(enclosingElement)) {
      parentAndChild.get(enclosingElement).add(element);
    } else {
      List<Element> children = new ArrayList<>();
      children.add(element);
      parentAndChild.put(enclosingElement, children);
    }
  }
}

TypeElement type_Injector = 
          mElementUtils.getTypeElement("com.lede.component.componentlib.inject.Injector");
TypeElement type_JsonService = 
         mElementUtils.getTypeElement("com.lede.component.componentlib.service.JsonService");

TypeElement parent = entry.getKey();
String fileName = parent.getSimpleName() + "$$Router$$InjectHelper";

TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
        .addJavadoc("Auto generated by " + TAG)
        .addSuperinterface(ClassName.get(type_Injector))
        .addModifiers(PUBLIC);

FieldSpec jsonServiceField = 
        FieldSpec.builder(
               TypeName.get(type_JsonService.asType()),
               "jsonService",
               Modifier.PRIVATE).build();
helper.addField(jsonServiceField);

ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();

MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder("inject")
        .addAnnotation(Override.class)
        .addModifiers(PUBLIC)
        .addParameter(objectParamSpec);

injectMethodBuilder.addStatement(
            "jsonService = $T.Factory.getSingletonImpl()",
            ClassName.get(type_JsonService));
injectMethodBuilder.addStatement(
            "$T substitute = ($T)target",
            ClassName.get(parent),
            ClassName.get(parent));

injectMethodBuilder.beginControlFlow("if (null != jsonService)");
injectMethodBuilder.addStatement(
            "substitute." + fieldName + " = " + statement,
            (StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
            ClassName.get(element.asType()));
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
            "$T.e(\"JsonService not found in Router\")", AndroidLog);
injectMethodBuilder.endControlFlow();

helper.addMethod(injectMethodBuilder.build());

JavaPoet简介:

  • $L:数值,addStatement("int value=$L", 10)
  • $S:字符串,如果被这个用这个占位符占位的会被2个双引号包裹住
  • $T:类型,这个会自动导包addStatement("$T obj=new $T()", Persion.class, Persion.class) 生成的就是new Persion(),接收一个class字节码
  • $N:引用,比如你有一个MethodSpec对象,如果你想调用这个方法
MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit")
    .addParameter(int.class, "i")
    .returns(char.class)
    .addStatement("return (char) (i < 10 ? i + '0' : i - 10 + 'a')")
    .build();

MethodSpec byteToHex = MethodSpec.methodBuilder("byteToHex")
    .addParameter(int.class, "b")
    .returns(String.class)
    .addStatement("char[] result = new char[2]")
    .addStatement("result[0] = $N((b >>> 4) & 0xf)", hexDigit)
    .addStatement("result[1] = $N(b & 0xf)", hexDigit)
    .addStatement("return new String(result)")
    .build();

public String byteToHex(int b) {
  char[] result = new char[2];
  result[0] = hexDigit((b >>> 4) & 0xf);
  result[1] = hexDigit(b & 0xf);
  return new String(result);
}

public char hexDigit(int i) {
  return (char) (i < 10 ? i + '0' : i - 10 + 'a');
}

MethodSpec出了可以用addStatement也可以使用addCode

MethodSpec main = MethodSpec.methodBuilder("main")
    .addCode(""
        + "int total = 0;\n"
        + "for (int i = 0; i < 10; i++) {\n"
        + "  total += i;\n"
        + "}\n")
    .build();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little-sparrow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值