目录
前言
组件,就是对数据和方法的简单封装,功能单一,高类聚,是业务划分的最小粒度。组件化是基于可重用的目的,将大型软件系统按照分离关注点的形式,拆分成多个独立组件,使得整个软件是单个或多个组件元件组装起来。那组件之间如何通信呢?这就得益于ARouter。
Android原生的路由方案是Intent的显式和隐式跳转,显式需要对目标的引用,会导致不同页面的耦合,隐式集中配置在manifest中,不利于维护和管理。况且,在组件化开发中,各模块之间无法直接引用,那么,ARouter路由框架就派上用场了。
一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
原理简述
ARouter通过APT技术,生成保存路径(路由path)和被注解(@Router)的组件类的映射关系的类,利用这些保存了映射关系的类,根据用户的请求postcard寻找到要跳转的目标地址,使用Intent跳转。所以,该框架的核心是利用APT生成的映射关系,APT的作用是在编译阶段扫描并处理代码中的注解,然后根据注解输出Java文件。
基本使用
添加依赖和配置,注意,每个使用到ARouter的Module都要引入
plugins {
id 'com.android.library'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'
引入后需要注意的一点是:要在gradle.properties文件中加入下面这个,不然会编译不过去,这也是我遇到的一个小坑。
android.enableJetifier=true
在Application中初始化
if (isDebug()) { ARouter.openLog() //打印日志 ARouter.openDebug() //开启调试模式,线上需关闭 } ARouter.init(this)
在支持路由的页面上添加如下的注解,路径至少需要两级
@Route(path = "/home/HomeActivity") class HomeActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) } }
然后在另一个Activity中,进行跳转
ARouter.getInstance().build("/home/HomeActivity").navigation()
如果需要传递参数的话,可以这样做
ARouter.getInstance().build("/home/HomeActivity") .withString("name", "Uncle Xing") .withInt("age", 25) .withSerializable("user", User("Uncle Xing", 25)) .navigation()
然后在目标Activity中通过Autowired接收,ARouter会自动对字段进行赋值,无需主动获取
@Route(path = "/home/HomeActivity") class HomeActivity : AppCompatActivity() { @JvmField @Autowired var name = "" @JvmField @Autowired var age = 0 @JvmField @Autowired var user: User? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_home) initView() } private fun initView() { ARouter.getInstance().inject(this) findViewById<TextView>(R.id.name).text = name findViewById<TextView>(R.id.age).text = age.toString() findViewById<TextView>(R.id.user).text = user.toString() } }
在组件化开发中,我们通常会有一些公共Module来作为共有功能,那这个时候就可以使用ARouter的依赖注入解耦,组件件的通信,首先我们要声明接口,其他组件通过这个接口来调用方法
interface MyProvider : IProvider { fun getData(): String }
实现类
@Route(path = "/common/MyProviderImpl") class MyProviderImpl : MyProvider { override fun getData(): String { return "Welcome to my blog" } override fun init(context: Context?) { } }
其他组件的Activity就可以这样调用
class MainActivity : AppCompatActivity() { /** * 当一个接口只有一个实现类的时候,Autowired可以不设置name */ @JvmField @Autowired(name = "/common/MyProviderImpl") var myProvider: MyProvider? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initView() } private fun initView() { ARouter.getInstance().inject(this) findViewById<TextView>(R.id.provider_text).text = myProvider?.getData() } }
上面是使用依赖注入的方式,通过注解标注字段,即可使用,无需主动获取,除此之外,我们也可以使用赖查找的方式,比如上面的代码我们也可以写成这样
class MainActivity : AppCompatActivity() { var myProvider: MyProvider? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initView() } private fun initView() { // ARouter.getInstance().inject(this) 这种方式不需要这句 myProvider = ARouter.getInstance().build("/common/MyProviderImpl").navigation() as MyProvider /** * 发现的方式有byName和byType,如果一个接口只有一个实现的话,也可以使用byType,可以写成 * myProvider = ARouter.getInstance().navigation(MyProvider::class.java) */ findViewById<TextView>(R.id.provider_text).text = myProvider?.getData() } }
我们也可以动态注册路由,这样,目标页面和服务就可以不标注 @Route 注解。不过,一般组件化项目都不会这样干,适合部分插件化架构的项目或其他场景。
ARouter.getInstance().addRouteGroup { it["/home/HomeActivity"] = RouteMeta.build( RouteType.ACTIVITY, //路由信息 HomeActivity::class.java, //目标class "/home/HomeActivity", //path "home", //Group,尽量保持和path的第一段相同 0, 0 ) }
注意:同一批次仅允许相同 group 的路由信息注册