美文网首页架构师之路
3. Android 一步步手写ARouter

3. Android 一步步手写ARouter

作者: 鹏城十八少 | 来源:发表于2021-12-24 15:13 被阅读0次

    CountDownLatch的使用!系统里面经常用到!

    跳转遇到的问题?Arounter需要解决的问题

    不能拿到对方的类。你只能通过找到类的路径去反射拿到这个class,那么有没有一种更好的解决办法呢?

    Arounter的原理:产生路由表,通过key去找到value

    分析:

    首先第一步,定义注解:

    Target:ElementType.Type

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">@Target(ElementType.TYPE)
    @Retention(RetentionPolicy.CLASS)
    public @interface Route {

    /**
    
    • 路由的路径 * * @return
      /
      String path(); /
      *
    • 将路由节点进行分组,可以实现动态加载 * * @return
      */
      String group() default ""; }
      </pre>

    为什么需要分组?

    Route注解里有path和group,这便是仿照ARouter对路由进行分组。因为当项目变得越来越庞大的时候,为了便于管理和减小首次加载路由表过于耗时的问题

    第二步:定义基本的接口

    **[图片上传失败...(image-c9e97f-1640330005823)]

    IRouteGroup:自定义加载接口。不是自动生成的,自定义的

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public interface IRouteGroup {
    void loadInto(Map<String, RouteMeta> atlas); }</pre>**

    第三步,编写注解处理器。

    需要生成的.java文件

    [图片上传失败...(image-11f849-1640330005863)]

    EaseRouter_Root_app.java: 里面需要是代表有几个模块,组。有2组

    EaseRouter_Group_main.java :包含了Main模块里面的跳转

    EaseRouter_Group_show.java :包含了show模块里面的跳转

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class EaseRouter_Root_app implements IRouteRoot {
    @Override
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("main", EaseRouter_Group_main.class);
    routes.put("show", EaseRouter_Group_show.class);
    }
    }
    </pre>

    EaseRouter_Group_main.java文件

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class EaseRouter_Group_main implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/main/main",RouteMeta.build(RouteMeta.Type.ACTIVITY,MainActivity.class,"/main/main","main"));
    atlas.put("/main/main2",RouteMeta.build(RouteMeta.Type.ACTIVITY,Main2Activity.class,"/main/main2","main"));
    }
    }</pre>

    EaseRouter_Group_show

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class EaseRouter_Group_show implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/show/info",RouteMeta.build(RouteMeta.Type.ACTIVITY,ShowActivity.class,"/show/info","show"));
    }
    }</pre>

    通过注解的形式重写几个方法,比butterknife的又强了点!

    @AutoService(Processor.class)
    /**
      处理器接收的参数 替代 {@link AbstractProcessor#getSupportedOptions()} 函数
     */
    @SupportedOptions(Constant.ARGUMENTS_NAME)
    /**
     * 注册给哪些注解的  替代 {@link AbstractProcessor#getSupportedAnnotationTypes()} 函数
     */
    @SupportedAnnotationTypes(Constant.ANNOTATION_TYPE_ROUTE)
    

    生成文件的方法:

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">/**

      • @param set 使用了支持处理注解的节点集合
    • @param roundEnvironment 表示当前或是之前的运行环境,可以通过该对象查找找到的注解。
    • @return true 表示后续处理器不会再处理(已经处理)
      */ @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
      if (!Utils.isEmpty(set)) {
      //被Route注解的节点集合
      Set<? extends Element> rootElements = roundEnvironment.getElementsAnnotatedWith(Route.class);
      if (!Utils.isEmpty(rootElements)) {
      processorRoute(rootElements);
      }
      return true;
      }
      return false; }

    private void processorRoute(Set<? extends Element> rootElements) {
    //获得Activity这个类的节点信息
    TypeElement activity = elementUtils.getTypeElement(Constant.ACTIVITY);
    TypeElement service = elementUtils.getTypeElement(Constant.ISERVICE);
    for (Element element : rootElements) {
    RouteMeta routeMeta;
    //类信息
    TypeMirror typeMirror = element.asType();
    log.i("Route class:" + typeMirror.toString());
    Route route = element.getAnnotation(Route.class);
    if (typeUtils.isSubtype(typeMirror, activity.asType())) {
    routeMeta = new RouteMeta(RouteMeta.Type.ACTIVITY, route, element);
    } else if (typeUtils.isSubtype(typeMirror, service.asType())) {
    routeMeta = new RouteMeta(RouteMeta.Type.ISERVICE, route, element);
    } else {
    throw new RuntimeException("Just support Activity or IService Route: " + element);
    }
    categories(routeMeta);
    }
    TypeElement iRouteGroup = elementUtils.getTypeElement(Constant.IROUTE_GROUP);
    TypeElement iRouteRoot = elementUtils.getTypeElement(Constant.IROUTE_ROOT); //生成Group记录分组表
    generatedGroup(iRouteGroup); //生成Root类 作用:记录<分组,对应的Group类>
    generatedRoot(iRouteRoot, iRouteGroup); }</pre>

    用2个Map保存信息!

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">/**

    • key:组名 value:类名 / private Map<String, String> rootMap = new TreeMap<>(); /*
    • 分组 key:组名 value:对应组的路由信息 */ private Map<String, List<RouteMeta>> groupMap = new HashMap<>();</pre>

    生成Group的java文件

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">private void generatedGroup(TypeElement iRouteGroup) {
    //创建参数类型 Map<String, RouteMeta>
    ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
    ClassName.get(Map.class),
    ClassName.get(String.class),
    ClassName.get(RouteMeta.class));
    ParameterSpec altas = ParameterSpec.builder(parameterizedTypeName, "atlas").build(); for (Map.Entry<String, List<RouteMeta>> entry : groupMap.entrySet()) {
    MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_INTO)
    .addModifiers(Modifier.PUBLIC)
    .addAnnotation(Override.class)
    .addParameter(altas); String groupName = entry.getKey();
    List<RouteMeta> groupData = entry.getValue();
    for (RouteMeta routeMeta : groupData) {
    //函数体的添加
    methodBuilder.addStatement("atlas.put(S,T.build(T.L,T.class,S,$S))",
    routeMeta.getPath(),
    ClassName.get(RouteMeta.class),
    ClassName.get(RouteMeta.Type.class),
    routeMeta.getType(),
    ClassName.get(((TypeElement) routeMeta.getElement())),
    routeMeta.getPath(),
    routeMeta.getGroup());
    }
    String groupClassName = Constant.NAME_OF_GROUP + groupName;
    TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName)
    .addSuperinterface(ClassName.get(iRouteGroup))
    .addModifiers(Modifier.PUBLIC)
    .addMethod(methodBuilder.build())
    .build();
    JavaFile javaFile = JavaFile.builder(Constant.PACKAGE_OF_GENERATE_FILE, typeSpec).build();
    try {
    javaFile.writeTo(filerUtils);
    } catch (IOException e) {
    e.printStackTrace();
    }
    rootMap.put(groupName, groupClassName); }
    }</pre>

    几个类:

    TypeElement:元素类型,比如activty,Service

    TypeMirror: 类信息

    注解处理器的源码:

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">@AutoService(Processor.class)
    /**
    处理器接收的参数 替代 {@link AbstractProcessor#getSupportedOptions()} 函数
    / @SupportedOptions(Constant.ARGUMENTS_NAME)
    /
    *

    • 指定使用的Java版本 替代 {@link AbstractProcessor#getSupportedSourceVersion()} 函数
      / @SupportedSourceVersion(SourceVersion.RELEASE_7)
      /
      *
    • 注册给哪些注解的 替代 {@link AbstractProcessor#getSupportedAnnotationTypes()} 函数
      */ @SupportedAnnotationTypes(Constant.ANNOTATION_TYPE_ROUTE)

    public class RouterProcessor extends AbstractProcessor {
    /**

    • key:组名 value:类名 / private Map<String, String> rootMap = new TreeMap<>();
      /
      *

    • 分组 key:组名 value:对应组的路由信息 / private Map<String, List<RouteMeta>> groupMap = new HashMap<>(); /*

    • 节点工具类 (类、函数、属性都是节点) / private Elements elementUtils; /*

    • type(类信息)工具类 / private Types typeUtils; /*

    • 文件生成器 类/资源 */ private Filer filerUtils; private String moduleName; private Log log; @Override
      public synchronized void init(ProcessingEnvironment processingEnvironment) {
      super.init(processingEnvironment);
      //获得apt的日志输出
      log = Log.newLog(processingEnvironment.getMessager());
      elementUtils = processingEnvironment.getElementUtils();
      typeUtils = processingEnvironment.getTypeUtils();
      filerUtils = processingEnvironment.getFiler(); //参数是模块名 为了防止多模块/组件化开发的时候 生成相同的 xxROOT文件
      Map<String, String> options = processingEnvironment.getOptions();
      if (!Utils.isEmpty(options)) {
      moduleName = options.get(Constant.ARGUMENTS_NAME);
      }
      if (Utils.isEmpty(moduleName)) {
      throw new RuntimeException("Not set processor moudleName option !");
      }
      log.i("init RouterProcessor " + moduleName + " success !");
      }

      /**

      • @param set 使用了支持处理注解的节点集合
    • @param roundEnvironment 表示当前或是之前的运行环境,可以通过该对象查找找到的注解。

    • @return true 表示后续处理器不会再处理(已经处理)
      */ @Override
      public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
      if (!Utils.isEmpty(set)) {
      //被Route注解的节点集合
      Set<? extends Element> rootElements = roundEnvironment.getElementsAnnotatedWith(Route.class);
      if (!Utils.isEmpty(rootElements)) {
      processorRoute(rootElements);
      }
      return true;
      }
      return false;
      }

      private void processorRoute(Set<? extends Element> rootElements) {
      //获得Activity这个类的节点信息
      TypeElement activity = elementUtils.getTypeElement(Constant.ACTIVITY);
      TypeElement service = elementUtils.getTypeElement(Constant.ISERVICE);
      for (Element element : rootElements) {
      RouteMeta routeMeta;
      //类信息
      TypeMirror typeMirror = element.asType();
      log.i("Route class:" + typeMirror.toString());
      Route route = element.getAnnotation(Route.class);
      if (typeUtils.isSubtype(typeMirror, activity.asType())) {
      routeMeta = new RouteMeta(RouteMeta.Type.ACTIVITY, route, element);
      } else if (typeUtils.isSubtype(typeMirror, service.asType())) {
      routeMeta = new RouteMeta(RouteMeta.Type.ISERVICE, route, element);
      } else {
      throw new RuntimeException("Just support Activity or IService Route: " + element);
      }
      categories(routeMeta);
      }
      TypeElement iRouteGroup = elementUtils.getTypeElement(Constant.IROUTE_GROUP);
      TypeElement iRouteRoot = elementUtils.getTypeElement(Constant.IROUTE_ROOT); //生成Group记录分组表
      generatedGroup(iRouteGroup); //生成Root类 作用:记录<分组,对应的Group类>
      generatedRoot(iRouteRoot, iRouteGroup);
      }

      /**

    • 生成Root类 作用:记录<分组,对应的Group类>

    • @param iRouteRoot

    • @param iRouteGroup
      */
      private void generatedRoot(TypeElement iRouteRoot, TypeElement iRouteGroup) {
      //创建参数类型 Map<String,Class<? extends IRouteGroup>> routes>
      //Wildcard 通配符 ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
      ClassName.get(Map.class),
      ClassName.get(String.class),
      ParameterizedTypeName.get(
      ClassName.get(Class.class),
      WildcardTypeName.subtypeOf(ClassName.get(iRouteGroup))
      ));
      //参数 Map<String,Class<? extends IRouteGroup>> routes> routes
      ParameterSpec parameter = ParameterSpec.builder(parameterizedTypeName, "routes").build();
      //函数 public void loadInfo(Map<String,Class<? extends IRouteGroup>> routes> routes)
      MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_INTO)
      .addModifiers(Modifier.PUBLIC)
      .addAnnotation(Override.class)
      .addParameter(parameter);
      //函数体
      for (Map.Entry<String, String> entry : rootMap.entrySet()) {
      methodBuilder.addStatement("routes.put(S,T.class)", entry.getKey(), ClassName.get(Constant.PACKAGE_OF_GENERATE_FILE, entry.getValue()));
      }
      //生成Root
      String className = Constant.NAME_OF_ROOT + moduleName;
      TypeSpec typeSpec = TypeSpec.classBuilder(className)
      .addSuperinterface(ClassName.get(iRouteRoot))
      .addModifiers(Modifier.PUBLIC)
      .addMethod(methodBuilder.build())
      .build();
      try {
      JavaFile.builder(Constant.PACKAGE_OF_GENERATE_FILE, typeSpec).build().writeTo(filerUtils);
      log.i("Generated RouteRoot:" + Constant.PACKAGE_OF_GENERATE_FILE + "." + className);
      } catch (IOException e) {
      e.printStackTrace();
      }
      }

      private void generatedGroup(TypeElement iRouteGroup) {
      //创建参数类型 Map<String, RouteMeta>
      ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(
      ClassName.get(Map.class),
      ClassName.get(String.class),
      ClassName.get(RouteMeta.class));
      ParameterSpec altas = ParameterSpec.builder(parameterizedTypeName, "atlas").build(); for (Map.Entry<String, List<RouteMeta>> entry : groupMap.entrySet()) {
      MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(Constant.METHOD_LOAD_INTO)
      .addModifiers(Modifier.PUBLIC)
      .addAnnotation(Override.class)
      .addParameter(altas); String groupName = entry.getKey();
      List<RouteMeta> groupData = entry.getValue();
      for (RouteMeta routeMeta : groupData) {
      //函数体的添加
      methodBuilder.addStatement("atlas.put(S,T.build(T.L,T.class,S,$S))",
      routeMeta.getPath(),
      ClassName.get(RouteMeta.class),
      ClassName.get(RouteMeta.Type.class),
      routeMeta.getType(),
      ClassName.get(((TypeElement) routeMeta.getElement())),
      routeMeta.getPath(),
      routeMeta.getGroup());
      }
      String groupClassName = Constant.NAME_OF_GROUP + groupName;
      TypeSpec typeSpec = TypeSpec.classBuilder(groupClassName)
      .addSuperinterface(ClassName.get(iRouteGroup))
      .addModifiers(Modifier.PUBLIC)
      .addMethod(methodBuilder.build())
      .build();
      JavaFile javaFile = JavaFile.builder(Constant.PACKAGE_OF_GENERATE_FILE, typeSpec).build();
      try {
      javaFile.writeTo(filerUtils);
      } catch (IOException e) {
      e.printStackTrace();
      }
      rootMap.put(groupName, groupClassName); }
      }

      /**

    • 检查是否配置 group 如果没有配置 则从path截取出组名 * @param routeMeta
      */
      private void categories(RouteMeta routeMeta) {
      if (routeVerify(routeMeta)) {
      log.i("Group : " + routeMeta.getGroup() + " path=" + routeMeta.getPath());
      //分组与组中的路由信息
      List<RouteMeta> routeMetas = groupMap.get(routeMeta.getGroup());
      if (Utils.isEmpty(routeMetas)) {
      routeMetas = new ArrayList<>();
      routeMetas.add(routeMeta);
      groupMap.put(routeMeta.getGroup(), routeMetas);
      } else {
      routeMetas.add(routeMeta);
      }
      } else {
      log.i("Group info error:" + routeMeta.getPath());
      }
      }

      /**

    • 验证path路由地址的合法性 * @param routeMeta

    • @return
      */
      private boolean routeVerify(RouteMeta routeMeta) {
      String path = routeMeta.getPath();
      String group = routeMeta.getGroup();
      // 必须以 / 开头来指定路由地址
      if (!path.startsWith("/")) {
      return false;
      }
      //如果group没有设置 我们从path中获得group
      if (Utils.isEmpty(group)) {
      String defaultGroup = path.substring(1, path.indexOf("/", 1));
      //截取出的group还是空
      if (Utils.isEmpty(defaultGroup)) {
      return false;
      }
      routeMeta.setGroup(defaultGroup);
      }
      return true;
      }

    }
    </pre>

    第四步: 文件生成后,需要调用,通过反射,需要反射去创建实例!

    先模拟调用的代码!

    需要2个Map去装载组和类

    public void test() {
        EaseRouter_Root_app rootApp = new EaseRouter_Root_app();
        HashMap<String, Class<? extends IRouteGroup>> rootMap = new HashMap<>();
        rootApp.loadInto(rootMap);
    
        //得到/main分组
        Class<? extends IRouteGroup> aClass = rootMap.get("main");
        try {
            HashMap<String, RouteMeta> groupMap = new HashMap<>();
            aClass.newInstance().loadInto(groupMap);
            //得到MainActivity
            RouteMeta main = groupMap.get("/main/main");
            Class<?> mainActivityClass = main.getDestination();
    
            Intent intent = new Intent(this, mainActivityClass);
            startActivity(intent);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    
    }
    

    具体:在application的时候去填充map

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">public class Warehouse {

    // root 映射表 保存分组信息
    

    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); // group 映射表 保存组中的所有数据
    static Map<String, RouteMeta> routes = new HashMap<>(); // group 映射表 保存组中的所有数据
    static Map<Class, IService> services = new HashMap<>();
    // TestServiceImpl.class , TestServiceImpl 没有再反射 /**

    • 以键值对优先级的方式保存拦截器对象 / public static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>();
      /
      *
    • 以集合的方式保存所有拦截器对象 */ public static List<IInterceptor> interceptors = new ArrayList<>(); }
      </pre>

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">/**

    • 分组表制作 */ private static void loadInfo() throws PackageManager.NameNotFoundException, InterruptedException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
      //获得所有 apt生成的路由类的全类名 (路由表)
      Set<String> routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
      for (String className : routerMap) {
      if (className.startsWith(ROUTE_ROOT_PAKCAGE + "." + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
      //root中注册的是分组信息 将分组信息加入仓库中
      ((IRouteRoot) Class.forName(className).getConstructor().newInstance()).loadInto(Warehouse.groupsIndex);
      } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + "." + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTOR)) {

           ((IInterceptorGroup) Class.forName(className).getConstructor().newInstance()).loadInto(Warehouse.interceptorsIndex);
      

    }
    }
    for (Map.Entry<String, Class<? extends IRouteGroup>> stringClassEntry : Warehouse.groupsIndex.entrySet()) {
    Log.d(TAG, "Root映射表[ " + stringClassEntry.getKey() + " : " + stringClassEntry.getValue() + "]");
    }

    }</pre>

    这里用到了CountDownLatch类,会分path一个文件一个文件的检索,等到所有的类文件都找到后便会返回这个Set<String>集合。

    注意,Warehouse就是专门用来存放路由映射关系的类,里面保存着存路由信息的map,这在ARouter里面也是一样的。这段代码Warehouse.routes.get(card.getPath())通过path拿到对应的RouteMeta,这个RouteMeta里面保存了activityClass等信息。继续往下看,如果判断拿到的RouteMeta是空,说明这个路由地址还没有加载到map里面(初始化时为了节省性能,只会加载所有的分组信息,而每个分组下的路由映射关系,会使用懒加载,在首次用到的时候去加载)

    下面就是用到了反射!!

    <pre style="margin: 8px 0px; background-color: rgb(43, 43, 43); color: rgb(169, 183, 198); font-family: "JetBrains Mono", monospace; font-size: 0.817rem;">/**

    • 准备卡片 * * @param card
      */ private void prepareCard(Postcard card) {
      RouteMeta routeMeta = Warehouse.routes.get(card.getPath());
      if (null == routeMeta) {
      Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(card.getGroup());
      if (null == groupMeta) {
      throw new NoRouteFoundException("没找到对应路由:分组=" + card.getGroup() + " 路径=" + card.getPath());
      }
      IRouteGroup iGroupInstance;
      try {
      iGroupInstance = groupMeta.getConstructor().newInstance();
      } catch (Exception e) {
      throw new RuntimeException("路由分组映射表记录失败.", e);
      }
      iGroupInstance.loadInto(Warehouse.routes);
      //已经准备过了就可以移除了 (不会一直存在内存中)
      Warehouse.groupsIndex.remove(card.getGroup());
      //再次进入 else
      prepareCard(card);
      } else {
      //类 要跳转的activity 或IService实现类
      card.setDestination(routeMeta.getDestination());
      card.setType(routeMeta.getType());
      switch (routeMeta.getType()) {
      case ISERVICE:
      Class<?> destination = routeMeta.getDestination();
      IService service = Warehouse.services.get(destination);
      if (null == service) {
      try {
      service = (IService) destination.getConstructor().newInstance();
      Warehouse.services.put(destination, service);
      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      card.setService(service);
      break; default:
      break;
      }
      }
      }</pre>

    最好的实现

    https://www.jianshu.com/p/857aea5b54a8

    相关文章

      网友评论

        本文标题:3. Android 一步步手写ARouter

        本文链接:https://www.haomeiwen.com/subject/orwlqrtx.html