组件化是基于可重用的目的,将大型软件系统按照分离关注点的形式,拆分成多个独立组件,使得整个软件是单个或多个组件元件组装起来。那组件之间如何通信呢?这就得益于 ARouter。
Android 原生的路由方案是 Intent 的显式和隐式跳转,显式需要对目标的引用,会导致不同页面的耦合,隐式集中配置在 Manifest 中,不利于维护和管理。况且,在组件化开发中,各模块之间无法直接引用,那么,ARouter 路由框架就派上用场了。
ARouter 是一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。
原理简述
ARouter 通过 APT 技术,生成保存路径和被注解的组件类的映射关系的类,利用这些保存了映射关系的类,根据用户的请求 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", "ARouter")
.withInt("age", 25)
.withSerializable("user", User("ARouter", 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
)
}