当前位置: 首页>编程语言>正文

3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT)

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

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

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

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

分析:

首先第一步,定义注解:

Target:ElementType.Type

<preJetBrains 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:自定义加载接口。不是自动生成的,自定义的

<preJetBrains 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模块里面的跳转

<preJetBrains 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文件

<preJetBrains 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

<preJetBrains 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)

生成文件的方法:

<preJetBrains 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保存信息!

<preJetBrains 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文件

<preJetBrains 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(3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),S,,第1张T.build(3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),T.,第2张L,3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),T.class,,第3张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: 类信息

注解处理器的源码:

<preJetBrains 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(); //参数是模块名 为了防止多模块/组件化开发的时候 生成相同的 xx3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),ROOT,第4张文件
    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(3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),S,,第1张T.class)", entry.getKey(), ClassName.get(Constant.PACKAGE_OF_GENERATE_FILE, entry.getValue()));
    }
    //生成3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),Root,第6张
    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(3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),S,,第1张T.build(3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),T.,第2张L,3. Android 一步步手写ARouter 3分钟搞定组件化注解+注解处理器 (APT),T.class,,第3张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

<preJetBrains 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>

<preJetBrains 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里面(初始化时为了节省性能,只会加载所有的分组信息,而每个分组下的路由映射关系,会使用懒加载,在首次用到的时候去加载)

下面就是用到了反射!!

<preJetBrains 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


https://www.xamrdz.com/lan/5rr1994413.html

相关文章: