架构师之路

3. Android 一步步手写ARouter

2021-12-24  本文已影响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 {

/**

为什么需要分组?

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;">/**

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;">/**

生成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)
/
*

public class RouterProcessor extends AbstractProcessor {
/**

}
</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 没有再反射 /**

<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;">/**

}
}
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;">/**

最好的实现

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

上一篇 下一篇

猜你喜欢

热点阅读