1、目的
- 路由框架是解决组件间相互依赖问题的。
- 组件化的有点方便拆分。假如A,B,C模块相互用到各自的App,那么一旦抽离B,那么A和C就有非常多要修改的地方,如果是100个相互依赖的模块呢?这明显就不太合理。这就是传说中的
拔出萝卜带出泥

2、怎么解决?
我把这个3个模块都依赖common,abc之间不相互依赖,那么A调用B,直接问 common就好了,是不是特别方便?
3、原理
AActivity1通过common调用B的Actiity2
- 可以通过Common里写的代码new Intent(A的Activity1.this,B的Activity2.class),但这个代码如果要人去写就太蠢了。能不能让机器帮我自动生成?
APT+JAVAPOET(带$$都是javapoet生成的文件)
- 也就是注解处理和很方便写java的工具。
- 注解大家都知道,我们整个注解是理器在编译时,找找咱们的注解,再配合javapoet生成class文件。
- 那么AActivity1通过common调用B的Actiity2只要用一个公共方法,传入
B/Actiity2
。他就会自动通过自动生成的class文件找到B/Actiity2的意思是返回Activity2.class
,注意这个是一个类,不是字符串。
4、实操
4.1、定义注解
- 先定义一个注解Arouter,目的是告诉注解处理器,我是哪个组件里的,叫啥名。
java
@Target(TYPE)
@Retention(CLASS)
public @interface ARoute {
String path();
String group() default "";
}
@ARoute(path = "/app/MainActivity",group = "app"),放到需要被使用跳转的app组件的MainActicity。
4.2、定义注解处理器
- 整个compiler组件吧。里边再写一个ARouterProcessor,
- 我只管这个注解的事情@SupportedAnnotationTypes({"com.kent.aroute_annotations.ARoute"}) // 注解
- 编译时他就干活了。init,编译就干活,process是有用了ARoute的事情我就干活。
java
@AutoService(Processor.class) // 启用服务
@SupportedAnnotationTypes({"com.kent.aroute_annotations.ARoute"}) // 注解
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 环境的版本
@SupportedOptions("student")
public class ARouterProcessor extends AbstractProcessor {
// 操作Element的工具类(类,函数,属性,其实都是Element)
private Elements elementTool;
// type(类信息)的工具类,包含用于操作TypeMirror的工具方法
private Types typeTool;
// Message用来打印 日志相关信息
private Messager messager;
// 文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的
private Filer filer;
private String options; // 各个模块传递过来的模块名 例如:app order personal
private String aptPackage; // 各个模块传递过来的目录 用于统一存放 apt生成的文件
// 仓库一 Path 缓存一
// Map<"personal", List<RouterBean>>
private Map<String, List<RouterBean>> mAllPathMap = new HashMap<>(); // 目前是一个
// 仓库二 Group 缓存二
// Map<"personal", "ARouter$$Path$$personal.class">
private Map<String, String> mAllGroupMap = new HashMap<>();
private String value;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementTool = processingEnv.getElementUtils();
messager = processingEnv.getMessager();
filer = processingEnv.getFiler();
value = processingEnv.getOptions().get("student");
aptPackage = processingEnv.getOptions().get(ProcessorConfig.APT_PACKAGE);
options = processingEnv.getOptions().get(ProcessorConfig.OPTIONS);
messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>>" + value + "!!!!" + aptPackage);
if (options != null && aptPackage != null) {
messager.printMessage(Diagnostic.Kind.NOTE, "APT 环境搭建完成....");
} else {
messager.printMessage(Diagnostic.Kind.NOTE, "APT 环境有问题,请检查 options 与 aptPackage 为null...");
}
}
public void logd(String s) {
messager.printMessage(Diagnostic.Kind.NOTE, s);
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>kent run ");
xxxxxxxxxxxxxxx
}
}
4.3、怎么去设计这些生成的文件?
- 生成一个保存我这个组件下所有需要被跳转的activity,也就是打了arouter注解的。
- app组件里有哪些activity上打了arouter组件的。我都存到hash里,B某个activity要跳转的时候,去common里找跳转方法
- 然后跳转方法通过传入的参数比如"app/Mainactivity",通过app找到了ARouter <math xmlns="http://www.w3.org/1998/Math/MathML"> G r o u p Group </math>Groupapp,因为生成的ARouter <math xmlns="http://www.w3.org/1998/Math/MathML"> G r o u p Group </math>Group文件 包名都是统一定义的。
- 找到ARouter <math xmlns="http://www.w3.org/1998/Math/MathML"> G r o u p Group </math>Groupapp就能找到 ARouter <math xmlns="http://www.w3.org/1998/Math/MathML"> P a t h Path </math>Pathapp,然后更具传入的参数"/app/MainActivity"找到 RouterBean,再找到MainActivity.class
java
public class ARouter$$Group$$app implements ARouterGroup {
@Override
public Map<String, Class<? extends ARouterPath>> getGroupMap() {
Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
groupMap.put("app", ARouter$$Path$$app.class);
return groupMap;
}
}
java
public class ARouter$$Path$$app implements ARouterPath {
@Override
public Map<String, RouterBean> getPathMap() {
Map<String,RouterBean> pathMap = new HashMap<>();
pathMap.put("/app/MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));
return pathMap;
}
}
5、跳转activitiy传参怎么办
5.1、再设计一个参数注解处理器Parameter
less
@Target(ElementType.FIELD) // 该注解作用在类之上 字段上有作用
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface Parameter {
// 不填写name的注解值表示该属性名就是key,填写了就用注解值作为key
// 从getIntent()方法中获取传递参数值
String name() default "";
}
5.1、发送端通过RouterManager.navigation讲要带的参数传过去
- 通过 RouterManager 参数存到bundleManager,最后在RouterManager完成跳转,
java
// 使用我们自己写的路由 跳转交互
RouterManager.getInstance()
.build("/personal/Personal_MainActivity")
.withString("name", "kent")
.withString("sex", "男")
.withInt("age", 99)
.withSerializable("student", student)
.navigation(this);
java
public class BundleManager {
// Intent传输 携带的值,保存到这里
private Bundle bundle = new Bundle();
// TODO 新增点
// 底层业务接口
private Call call;
Call getCall() {
return call;
}
void setCall(Call call) {
this.call = call;
}
public Bundle getBundle() {
return this.bundle;
}
java
public class BundleManager {
// 对外界提供,可以携带参数的方法
public BundleManager withString(@NonNull String key, @Nullable String value) {
bundle.putString(key, value);
return this; // 链式调用效果 模仿开源框架
}
public BundleManager withSerializable(@NonNull String key, @Nullable Serializable object) {
bundle.putSerializable(key, object);
return this;
}
java
public Object navigation(Context context, BundleManager bundleManager) {
// 例如:寻找 ARouter$$Group$$personal 寻址 ARouter$$Group$$order ARouter$$Group$$app
switch (routerBean.getTypeEnum()) {
case ACTIVITY:
Intent intent = new Intent(context, routerBean.getMyClass()); // 例如:getClazz == Order_MainActivity.class
intent.putExtras(bundleManager.getBundle()); // 携带参数
// context.startActivity(intent, bundleManager.getBundle()); // 大部分手机有问题,没有任何反应
context.startActivity(intent);
break;
case CALL:
// OrderAddressImpl.class OrderBean getOrderBean
Class<?> clazz = routerBean.getMyClass(); // OrderUserImpl BaseUser实体
Call call = (Call) clazz.newInstance();
bundleManager.setCall(call);
return bundleManager.getCall();
//同学们可以自己扩展 类型
}
}
5.2、接收端就像使用xutils一样给各个注解得变量设值
- 平常我们会用 name = t.getIntent().getStringExtra("name");来赋值
- 现在肯定用自动生成一个文件,调用公共方法ParameterManager.getInstance().loadParameter(this)。通过 Class.forName(activity全类名+"$$Parameter");找到文件。然后 赋值。
java
@ARouter(path = "/personal/Personal_MainActivity")
public class Personal_MainActivity extends AppCompatActivity {
@Parameter
String name; // 序列号 String
@Parameter
String sex;
@Parameter
int age = 9; // 序列号 int
@Parameter
Student student;
```
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.personal_activity_main);
// 模仿xutils
// bind(this);
ParameterManager.getInstance().loadParameter(this);
java
public class Personal_MainActivity$$Parameter implements ParameterGet {
@Override
public void getParameter(Object targetParameter) {
Personal_MainActivity t = (Personal_MainActivity) targetParameter;
t.name = t.getIntent().getStringExtra("name");
t.sex = t.getIntent().getStringExtra("sex");
t.age = t.getIntent().getIntExtra("age", t.age);
t.student=(Student)t.getIntent().getSerializableExtra("student");
t.orderDrawable = (OrderDrawable) RouterManager.getInstance().build("/order/getDrawable").navigation(t);
t.orderAddress = (OrderAddress) RouterManager.getInstance().build("/order/getOrderBean").navigation(t);
}
}
6、组件间怎么传图片?
- 在common中定义Call接口,再用一个接口继承Call,比如我要传一个图片,则创建OrderDrawable extends Call
java
public interface OrderDrawable extends Call {
int getDrawable();
}
- 再在order种实现接口暴露出来
java
// order 自己决定 自己的暴漏
@ARouter(path = "/order/getDrawable")
public class OrderDrawableImpl implements OrderDrawable {
@Override
public int getDrawable() {
return R.drawable.ic_ac_unit_black_24dp;
}
}
- 在哪里用就在哪里加上 @Parameter(name = "/order/getUserInfo")
java
@ARouter(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {
@Parameter(name = "/order/getDrawable")
OrderDrawable orderDrawable; // 公共基础库common
...
```
// 懒加载方式,跳到哪加载哪个类
ParameterManager.getInstance().loadParameter(this);
- 然后就会自动赋值,
java
public class MainActivity$$Parameter implements ParameterGet {
@Override
public void getParameter(Object targetParameter) {
MainActivity t = (MainActivity) targetParameter;
t.orderDrawable = (OrderDrawable) RouterManager.getInstance().build("/order/getDrawable").navigation(t);
t.iUser = (IUser) RouterManager.getInstance().build("/order/getUserInfo").navigation(t);
}
- 原理就是根据注解找到了ARouter <math xmlns="http://www.w3.org/1998/Math/MathML"> P a t h Path </math>Pathorder,然后得到Map<String, RouterBean>,再通过/order/getDrawable得到RouterBean,最后得到OrderDrawableImpl.class,由于都是实现了Call,
java
public class ARouter$$Path$$order implements ARouterPath {
@Override
public Map<String, RouterBean> getPathMap() {
Map<String, RouterBean> pathMap = new HashMap<>();
pathMap.put("/order/getOrderBean", RouterBean.create(RouterBean.TypeEnum.CALL, OrderAddressImpl.class, "/order/getOrderBean", "order"));
pathMap.put("/order/getDrawable", RouterBean.create(RouterBean.TypeEnum.CALL, OrderDrawableImpl.class, "/order/getDrawable", "order"));
pathMap.put("/order/getUserInfo", RouterBean.create(RouterBean.TypeEnum.CALL, OrderUserImpl.class, "/order/getUserInfo", "order"));
pathMap.put("/order/Order_MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, Order_MainActivity.class, "/order/Order_MainActivity", "order"));
return pathMap;
}
}
- 给人返回去就行。 t.orderDrawable = (OrderDrawable) RouterManager.getInstance().build("/order/getDrawable").navigation(t);
java
public Object navigation(Context context, BundleManager bundleManager) {
// 例如:寻找 ARouter$$Group$$personal 寻址 ARouter$$Group$$order ARouter$$Group$$app
switch (routerBean.getTypeEnum()) {
case ACTIVITY:
Intent intent = new Intent(context, routerBean.getMyClass()); // 例如:getClazz == Order_MainActivity.class
intent.putExtras(bundleManager.getBundle()); // 携带参数
// context.startActivity(intent, bundleManager.getBundle()); // 大部分手机有问题,没有任何反应
context.startActivity(intent);
break;
case CALL:
// OrderAddressImpl.class OrderBean getOrderBean
Class<?> clazz = routerBean.getMyClass(); // OrderUserImpl BaseUser实体
Call call = (Call) clazz.newInstance();
bundleManager.setCall(call);
return bundleManager.getCall();
//同学们可以自己扩展 类型
}
}
- 使用 嘿嘿
java
int drawableId = orderDrawable.getDrawable();
ImageView img = findViewById(R.id.img);
img.setImageResource(drawableId);
7、总结
- 建立一个common,让所有组件都依赖它。
- 将每个组件要暴露的文件都打上注解
- 然后注解处理器监听编译,为每个组件用javapoet创建一些文件,将需要被调用的类都暴露出去,有activity,有call。
- 那么A调用B的类,则通过A调用common,common根据传的参数,根据javapoet创建的B的暴露文件,找到需要返回的类。然后根据类型做对应操作,