1、意图

  • 路由框架是处理组件间相互依靠问题的。
  • 组件化的有点便利拆分。假设A,B,C模块相互用到各自的App,那么一旦抽离B,那么A和C就有十分多要修改的当地,假如是100个相互依靠的模块呢?这显着就不太合理。这便是传说中的拔出萝卜带出泥

模仿ARouter写一个路由框架

2、怎样处理?

我把这个3个模块都依靠common,abc之间不相互依靠,那么A调用B,直接问 common就好了,是不是特别便利?

模仿ARouter写一个路由框架

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,意图是告诉注解处理器,我是哪个组件里的,叫啥名。
@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的事情我就干活。
@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找到了ARouterGroupGroupapp,因为生成的ARouterGroupGroup文件 包名都是一致界说的。
  • 找到ARouterGroupGroupapp就能找到 ARouterPathPathapp,然后更具传入的参数”/app/MainActivity”找到 RouterBean,再找到MainActivity.class
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;
    }
}
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

@Target(ElementType.FIELD) // 该注解作用在类之上 字段上有作用
@Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作,注解会在class文件中存在
public @interface Parameter {
    // 不填写name的注解值表明该特点名便是key,填写了就用注解值作为key
    // 从getIntent()办法中获取传递参数值
    String name() default "";
}

5.1、发送端经过RouterManager.navigation讲要带的参数传过去

  • 经过 RouterManager 参数存到bundleManager,最后在RouterManager完结跳转,
// 运用咱们自己写的路由 跳转交互
RouterManager.getInstance()
        .build("/personal/Personal_MainActivity")
        .withString("name", "kent")
        .withString("sex", "男")
        .withInt("age", 99)
        .withSerializable("student", student)
        .navigation(this);
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;
    }
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;
}
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”);找到文件。然后 赋值。
@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);
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
public interface OrderDrawable extends Call {
    int getDrawable();
}
  • 再在order种完成接口露出出来
// 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”)
@ARouter(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {
    @Parameter(name = "/order/getDrawable")
    OrderDrawable orderDrawable; // 公共基础库common
    ...
    ```
// 懒加载方法,跳到哪加载哪个类
ParameterManager.getInstance().loadParameter(this);
  • 然后就会主动赋值,

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);
  }
  • 原理便是依据注解找到了ARouterPathPathorder,然后得到Map<String, RouterBean>,再经过/order/getDrawable得到RouterBean,最后得到OrderDrawableImpl.class,由于都是完成了Call,
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);
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();
        //同学们能够自己扩展 类型
    }
}
  • 运用 嘿嘿
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的露出文件,找到需要回来的类。然后依据类型做对应操作,