结合前面的模块之间的交互,主要分析了类加载、全局Map记录(跳转目标Class)两种交互方式,以及APT和JavaPoet技术生成类文件,本文分析组件化的路由架构,我们到底需要通过APT和JavaPoet生成什么样的类文件呢?
先来看这样一幅架构设计图,组文件记录是一对一的即一个子模块对应一个组文件记录,而一个组文件记录对应多个路径文件记录,第二点比较好理解,因为各个子模块有多个Activity,对应多个路径信息。对应第一点,为什么要生成一对一的组(名)文件记录呢?试想一下,如果用户进入首页,没有进行后续操作,退出应用。而我们还像之前分析的,在application中将全部的activity对应的路径信息记录到Map集合,加载到内存,这显然是不合理的,出于性能优化(内存优化)的考虑,只有用到对应组(模块)时,才去保存对应模块下Activity的路径信息。所以才会根据组(模块)名,生成对应的文件记录。要通过APT和JavaPoet生成哪些类文件呢?
在前面的文章中通过全局Map记录方案实现子模块交互时,有涉及到PathBean对象,主要包含目标Activity的路径path,和Class属性。获取class属性,即可实现交互(跳转)。而RouterBean对象就是在PathBean的基础上进行了扩展:
public class RouterBean {
// 枚举,现在只是处理作用在Activity上的注解
// 后续还会扩展处理接口上的注解
public enum Type{
ACTIVITY
}
//枚举类型
private Type type;
//类节点
private Element element;
//路由的组名
private String group;
//路由的地址
private String path;
//被@ARouter注解的类对象
private Class<?> clazz;
private RouterBean(Builder builder) {
this.type = builder.type;
this.element = builder.element;
this.clazz = builder.clazz;
this.path = builder.path;
this.group = builder.group;
}
...
}
Element类节点为什么也要存起来?
/**
* 路由组group对外提供加载数据接口
*/
public interface ARouterLoadGroup {
/**
* 加载路由组group数据
* 比如:"app", ARouter$$Path$$app.class(实现了ARouterLoadPath接口)
* @return key: "app"即组名, value: "app"分组对应的路由详细对象类
*/
Map<String, Class<? extends ARouterLoadPath>> loadGroup();
}
Map
/**
* 路由组group对应的详细path加载数据接口
* 比如:app分组对应有哪些类需要加载
*/
public interface ARouterLoadPath{
/**
* 加载路由组group中的path详细数据
* 比如:app分组下有这些信息
* @return key: "app/MainActivity", value:MainActivity信息封装到RouterBean对象中
*/
Map<String, RouterBean> loadPath();
}
Map
相关的配置问题可以参考前面的文章和文末代码链接查看。这里重点介绍模拟生成的类文件中的逻辑,以及跳转逻辑。在app模块中新建一个目录test,存放模拟apt生成的文件,就是自己手动创建的,主要是为了对流程进行说明,后续会通过apt来生成文件。不同模块对应文件中的逻辑是一样的
/**
* 模拟ARouter路由器的组文件
*/
public class ARouter$$Group$$order implements ARouterLoadGroup {
@Override
public Map<String, Class<? extends ARouterLoadPath>> loadGroup() {
// key: 组名,value: 分组对应的路由详细对象类
Map<String, Class<? extends ARouterLoadPath>> groupMap = new HashMap<>();
groupMap.put("order", ARouter$$Path$$order.class);
return groupMap;
}
}
返回的是map集合,key表示组名比如order,value表示分组(即模块)对应路由详细对象类
/**
* 模拟ARouter路由器的组文件对应的路径
*/
public class ARouter$$Path$$order implements ARouterLoadPath {
@Override
public Map<String, RouterBean> loadPath() {
Map<String, RouterBean> pathMap = new HashMap<>();
pathMap.put("/order/Order_MainActivity",
RouterBean.create(RouterBean.Type.ACTIVITY, Order_MainActivity.class,
"/order/Order_MainActivity", "order"));
return pathMap;
}
}
返回的是map集合,key表示路径信息,value表示将目标Activity的信息封装到RouterBean对象,可以从中获取对应的class对象。如果这部分不是很理解,看了跳转逻辑,应该就比较清晰了。
在app的MainActivity中,跳转逻辑如下:
public void jumpOrder(View view) {
/*Intent intent = new Intent(this, OrderActivity.class);
intent.putExtra("name", "xpf");
startActivity(intent);*/
// 模拟APT生成的类文件,实现跳转
ARouterLoadGroup loadGroup = new ARouter$$Group$$order();
// 只会加载order模块Activity对应的类对象到内存,避免将所有模块的Activity对应的类对象加载到内存
// 这就是处于性能优化(准确的说是内存优化),要有分组保存、获取的原因
Map<String, Class<? extends ARouterLoadPath>> groupMap = loadGroup.loadGroup();
// app--->order
Class<? extends ARouterLoadPath> clazz = groupMap.get("order");
try {
ARouterLoadPath path = clazz.newInstance();
Map<String, RouterBean> pathBean = path.loadPath();
// 获取/order/OrderActivity
RouterBean routerBean = pathBean.get("/order/OrderActivity");
if (routerBean != null) {
Intent intent = new Intent(this, routerBean.getClazz());
intent.putExtra("name", "xpf");
startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
首先实例化路由组文件,调用loadGroup方法获取保存的order模块的路径文件,通过它可以获取order模块Activity对应的类对象即class对象,然后实现跳转。如注释处所描述的,此时personal模块的所有Activity对应的类对象都不会加载到内存,这就是在性能方面的一个小优化,准确的说是内存优化。如果模块越多,每个模块的Activity越多,这个优化的作用就明显。只有需要进行跳转的时候,才会去加载对应模块Activity的类对象。
效果演示:
本文分析组件化的路由架构思路,也就是我们要通过APT生成什么样的文件,并手动实现演示可行性。下一篇将分析通过APT生成具体相关路由的类文件来实现。
代码链接:https://github.com/xpf-android/Moudlar_ARouter