从组件化引入路由设计需要满足的条件 - Xiasm/EasyRouter GitHub Wiki

为什么在这里要介绍组件化呢?因为路由天生契合组件化。并不是说路由是为组件化而设计的,实际上两者没有任何关系。

组件化需要路由设计满足的条件

标题很绕口,组件化是最近比较流行的架构思想,组件化使业务逻辑高度的解耦、模块分离等,同时也使多人协作并行开发变得更加简单,每个人只需要负责自己的模块就好。下面我放一张图来使大家更好地理解:

如上图,在组件化中,为了业务逻辑的彻底解耦,同时也为了每个module都可以方便的单独运行和调试,上层的各个module不会进行相互依赖(只有在正式联调的时候才会让app壳module去依赖上层的其他组件module),而是共同依赖于base module,base module中会依赖一些公共的第三方库和其他配置。
在开发阶段,我们的组件模块都是application的形式存在的,各个模块单独编译和运行,互不依赖,而在项目正式合并打线上包的时候,又要求组件模块以library的形式被主模块依赖。那么在开发阶段上层的各个module中,如何进行通信呢?

我们知道,传统的Activity之间通信,通过startActivity(intent),而在组件化的项目中,上层的module没有依赖关系(即便两个module有依赖关系,也只能是单向的依赖),那么假如login module中的一个Activity需要启动pay_module中的一个Activity便不能通过startActivity来进行跳转。那么大家想一下还有什么其他办法呢? 可能有同学会想到隐式跳转,这当然也是一种解决方法,但是一个项目中不可能所有的跳转都是隐式的,这样Manifest文件会有很多过滤配置,而且非常不利于后期维护。当然你用反射拿到Activity的class文件也可以实现跳转,但是第一:大量的使用反射跳转对性能会有影响,第二:你需要拿到Activity的类文件,在组件开发的时候,想拿到其他module的类文件是很麻烦的,因为组件开发的时候组件module之间是没有相互引用的,你只能通过找到类的路径去反射拿到这个class,那么有没有一种更好的解决办法呢?办法当然是有的。下面看图:

在组件化中,我们通常都会在base_module上层再依赖一个router_module,而这个router_module就是负责各个模块之间页面跳转的。
用过ARouter路由框架的同学应该都知道,在每个需要对其他module提供调用的Activity中,都会声明类似下面@Route注解,我们称之为路由地址:

@Route(path = "/main/main")
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}


@Route(path = "/module1/module1main")
public class Module1MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_module1_main);
    }
}

那么这个注解有什么用呢,路由框架会在项目的编译器扫描所有添加@Route注解的Activity类,然后将route注解中的path地址和Activity.class文件一一对应保存,如直接保存在map中。为了让大家理解,我这里来使用近乎伪代码给大家简单演示一下。

//项目编译后通过apt生成如下方法
public HashMap<String, ClassBean> routeInfo() {
    HashMap<String, ClassBean> route = new HashMap<String, ClassBean>();
    route.put("/main/main", MainActivity.class);
    route.put("/module1/module1main", Module1MainActivity.class);
    route.put("/login/login", LoginActivity.class);
}

这样我们想在app模块的MainActivity跳转到login模块的LoginActivity,那么便只需调用如下:

//不同模块之间启动Activity
public void login(String name, String password) {
    HashMap<String, ClassBean> route = routeInfo();
    LoginActivity.class classBean = route.get("/login/login");
    Intent intent = new Intent(this, classBean);
    intent.putExtra("name", name);
    intent.putExtra("password", password);
    startActivity(intent);
}

用过ARouter的同学应该知道,用ARouter启动Activity应该是下面这个写法

// 2. Jump with parameters
ARouter.getInstance().build("/test/login")
			.withString("password", 666666)
			.withString("name", "小三")
			.navigation();

那么ARouter背后的原理是怎么样的呢?实际上它的核心思想跟上面讲解的是一样的,我们在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activityClass映射关系的类文件,然后app进程启动的时候会拿到这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。

路由的多种用途

实际上,路由不是为组件化而生,因为它的用处很多,比如配合webview中打开客户端原生页面,配合后台打开不同的页面等。而类似ARouter这种路由呢,还有另一个功能,就是业务解耦后各个业务线的相互调用,我将它理解为业务路由,比如支付模块需要用到用户模块的用户信息,那么为了解耦,用户模块只会给外部声明调用用户信息的方法,并不关心谁要用到用户信息。而支付模块看到你的声明,就知道你有获取用户信息这个功能,调用即可。而这个功能的实现就需要依赖ARouter中的依赖注入了,我们最后几节会说到。

⚠️ **GitHub.com Fallback** ⚠️