上一篇我們使用了一個全局Map緩存來所有的Activity類,顯然這是非常麻煩的,一旦有所改動,就要手動修改該Map
為此,我們希望將key和Activity類的映射關(guān)系,通過一定方式自動導(dǎo)入Map。利用注解解析器(APT)和代碼生成器(kotlinpoet)可以根據(jù)注解在編譯期間就生成相應(yīng)的代碼,業(yè)界稱之為Router機制
一、Gradle配置及架構(gòu)分層
在實現(xiàn)Router機制之前,我們還可以對項目的組織架構(gòu)進行優(yōu)化,將gradle中公用部分抽出來
有了上一篇的基礎(chǔ),我們初步實現(xiàn)了架構(gòu)分層,目前有三個module:

其依賴關(guān)系為:app << libmodule_a << libase,但是每個module的gradle中都有重復(fù)的內(nèi)容,如版本號、版本名、SDK版本、重復(fù)依賴等,我們可以利用groovy和gradle的知識,為它們設(shè)計成共用屬性
1.創(chuàng)建config.gradle
在工程下新建一個config.gradle文件

將重復(fù)的內(nèi)容設(shè)置成全局屬性:
ext {
isDebug = false
kotlinVersion = "1.5.31"
// 版本信息
androidVersion = [
compileSdk : 31,
minSdk : 21,
targetSdk : 31,
versionCode: 1,
versionName: '1.0'
]
applicationId = [
app : "com.aruba.arouterapplication",
module_a: "com.aruba.libmodule_a"
]
androidxCore = 'androidx.core:core-ktx:1.3.2'
androidAppCompat = 'androidx.appcompat:appcompat:1.2.0'
androidMaterial = 'com.google.android.material:material:1.3.0'
androidConstraintLayout = 'androidx.constraintlayout:constraintlayout:2.0.4'
}
2.在主工程Gradle中,引入config.gradle
apply from: 'config.gradle'
buildscript {
repositories {
...
3.改造module的gradle
libbase的gradle改造為:
plugins {
id 'com.android.library'
id 'kotlin-android'
}
//使用一個變量,減少代碼量
def config = rootProject.ext
android {
compileSdk config.androidVersion.compileSdk
defaultConfig {
minSdk config.androidVersion.minSdk
targetSdk config.androidVersion.targetSdk
versionCode config.androidVersion.versionCode
versionName config.androidVersion.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
api androidxCore
api androidAppCompat
api androidMaterial
api androidConstraintLayout
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
libmoudle_a的gradle改造為:
def config = rootProject.ext
if (!config.isDebug) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
apply plugin: 'kotlin-android'
android {
compileSdk config.androidVersion.compileSdk
defaultConfig {
if (config.isDebug) {
applicationId config.applicationId.module_a
}
minSdk config.androidVersion.minSdk
targetSdk config.androidVersion.targetSdk
versionCode config.androidVersion.versionCode
versionName config.androidVersion.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
//根據(jù)變量配置不同manifest
sourceSets {
main {
if (!config.isDebug) {
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
api project(path: ':libbase')
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
app的gradle改造為:
plugins {
id 'com.android.application'
id 'kotlin-android'
}
def config = rootProject.ext
android {
compileSdk config.androidVersion.compileSdk
defaultConfig {
applicationId config.applicationId.app
minSdk config.androidVersion.minSdk
targetSdk config.androidVersion.targetSdk
versionCode config.androidVersion.versionCode
versionName config.androidVersion.versionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(path: ':libmodule_a')
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
4.歸納module路徑
項目后續(xù)可能會有很多個module,如果你想單獨使用文件夾進行分類,比如基礎(chǔ)的組件放入基礎(chǔ)的文件夾下,可以在settings.gradle中為module重新設(shè)置新路徑
我將libbase放入了新建文件夾lib_comm下:

修改settings.gradle配置:
include ':libbase'
project(':libbase').projectDir = new File('lib_comm/libbase')
其他的module也可以用該方法歸類
二、定義注解
要用到APT,那么肯定要自定義注解,來指定APT解析的注解
1.新建一個AnnotationModule
該module會被業(yè)務(wù)module和插件moudle依賴

2.定義Router注解
在需要跳轉(zhuǎn)的Activity上使用該注解,使用group和path來區(qū)分需要跳轉(zhuǎn)的目標(biāo)
/**
* 表示一個跳轉(zhuǎn)目標(biāo)(Activity、fragment)需要加入路由表
* Created by aruba on 2021/11/22.
*/
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
annotation class Router(
val group: String,//表示組,不可為空
val path: String//表示目標(biāo),不可重復(fù)
)
3.定義Router包裝類
包裝類用來最后跳轉(zhuǎn)使用,里面主要是存放著被注解的Activity類
/**
* 路由包裝類
* Created by aruba on 2021/11/22.
*/
data class RouterMeta(
val type: Type = Type.ACTIVITY,//類型
val path: String,//路由注解的path
val group: String,//路由注解的group
val clazz: Class<*>? = null//類對象
) {
var element: Element? = null//節(jié)點
enum class Type {
ACTIVITY,
ISERVICE
}
}
三、Router組件
上面我們的注解類中定義了group和path,還有RouterMeta包裝類,最后生成的Map的對應(yīng)關(guān)系如下圖:

上面的是一個group對應(yīng)一個IPathRouter生成類,IPathRouter是一個接口,我們會在Router組件中定義出來,下面是一個path對應(yīng)一個RouterMeta包裝類
這兩個Map,用來替換我們上一篇中,自己要手動導(dǎo)入Activity關(guān)系的Map,看到這你也許會懵,沒關(guān)系往下看接口的定義
1.新建Router Module
Route組件是實現(xiàn)功能的中間件,業(yè)務(wù)組件通過調(diào)用該組件方法來進行跳轉(zhuǎn)
依賴以下包:
dependencies {
implementation androidAppCompat
implementation kotlinCoroutine
api project(path: ':librouter_annotation')
}
2.定義接口
2.1 path-RouterMeta映射關(guān)系
首先是需要往一個map里存入path-RouterMeta映射關(guān)系
IRouterPath:
/**
* 根據(jù)path集合將RouterMeta存入緩存map
* Created by aruba on 2021/11/23.
*/
interface IRouterPath {
public fun cacheInRouterMetaByPath(map: LinkedHashMap<String, RouterMeta>)
}
為了方便理解,寫一個測試類來實現(xiàn)該接口,我們最后通過kotlinpoet生成的類也是參考該實現(xiàn)類:
class RouterPathTest : IRouterPath {
override fun cacheInRouterMetaByPath(map: LinkedHashMap<String, RouterMeta>) {
map["path1"] = RouterMeta(
type = RouterMeta.Type.ACTIVITY,
path = "path1",
group = "group1",
clazz = TestActivity::class.java
)
map["path2"] = RouterMeta(
type = RouterMeta.Type.ACTIVITY,
path = "path2",
group = "group1",
clazz = Test2Activity::class.java
)
...
}
}
就是在上一篇中,我們手動將映射關(guān)系存入Map的操作
2.2 group-IPathRouter映射關(guān)系
為了方便管理和擴展,我們引入了group的概念,group類似IRouterPath實現(xiàn)類的代理,一個group對應(yīng)一個IRouterPath實現(xiàn)類,最終我們通過group將IRouterPath實現(xiàn)類存入Map
IRouterGroup:
/**
* 根據(jù)group將IRouterPath存入緩存map
* Created by aruba on 2021/11/23.
*/
interface IRouterGroup {
public fun cacheInRouterPathByGroup(map: LinkedHashMap<String, Class<out IRouterPath>>)
}
同樣的寫一個測試類,IRouterGroup 的實現(xiàn)就簡單了,只需要一對一的關(guān)系:
class RouterGroupTest : IRouterGroup {
override fun cacheInRouterPathByGroup(map: LinkedHashMap<String, Class<out IRouterPath>>) {
map["group1"] = RouterPathTest::class.java
}
}
3.定義全局緩存
第二步我們需要往兩個Map中存入映射關(guān)系,來獲取跳轉(zhuǎn)時對應(yīng)的類,現(xiàn)在把它們定義出來
/**
* 映射關(guān)系緩存
* Created by aruba on 2021/11/23.
*/
object CacheMap {
// 根據(jù)group可以獲取到RouterPath
val RouterPathByGroup: LinkedHashMap<String, Class<out IRouterPath>> = LinkedHashMap()
// 根據(jù)path可以獲取到RouterMeta
val RouterMetaByPath: LinkedHashMap<String, RouterMeta> = LinkedHashMap()
}
四、APT+kotlinpoet自動生成類
有了上面的接口和全局緩存,我們就需要自動生成兩個實現(xiàn)類了
1.新建插件Module

2.配置Gradle
需要支持APT和kotlinpoet
plugins {
id 'java-library'
id 'kotlin'
id 'kotlin-kapt'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"
implementation project(path: ':librouter_annotation')
// apt依賴
compileOnly "com.google.auto.service:auto-service:1.0-rc7"
kapt "com.google.auto.service:auto-service:1.0-rc7"
// kotlinpoet
implementation group: 'com.squareup', name: 'kotlinpoet', version: '1.10.2'
}
3.定義一些全局變量
APT解析節(jié)點和kotlinpoet代碼生成時需要用到:類的包名和類名、方法名、生成的文件名(也是類名)、生成的類的包路徑等
object Const {
const val ACTIVITY = "android.app.Activity"
const val ISERVICE = "android.app.Fragment"
// 生成的類實現(xiàn)的接口
const val IROUTER_GROUP = "com.aruba.librouter_router.`interface`.IRouterGroup"
const val IROUTER_PATH = "com.aruba.librouter_router.`interface`.IRouterPath"
// 接口的package
const val IROUTER_PACKAGE = "com.aruba.librouter_router.`interface`"
// 接口的simplename
const val IROUTER_GROUP_SIMPLENAME = "IRouterGroup"
const val IROUTER_PATH_SIMPLENAME = "IRouterPath"
//接口需要實現(xiàn)的方法
const val METHOD_IROUTER_PATH = "cacheInRouterMetaByPath"
const val METHOD_IROUTER_GROUP = "cacheInRouterPathByGroup"
//生成類名的string format
const val FILENAME_FORMAT_PATH = "%s_%s_path"
const val FILENAME_FORMAT_GROUP = "%s_%s_group"
// 生成的類文件前綴
const val FILENAME_PREFIX = "Router"
// 生成的類的package
const val GENERATE_PACKAGE = "com.aruba.router"
}
4.使用注解解釋器及kotlinpoet
每個使用了插件的業(yè)務(wù)module都會執(zhí)行一次注解解釋器的方法,我們對注解的處理主要分為兩步:
- 使用APT獲取Router注解的類,并進行包裝,最后存入一個group-RouterMeta列表的Map中
- 對group-RouterMeta列表的Map進行處理,首先遍歷RouterMeta列表,使用kotlinpoet生成IRouterPath的實現(xiàn)類,再根據(jù)group和IRouterPath實現(xiàn)類的文件名(類名),生成實現(xiàn)IRouterGroup的類
/**
* 注解解析器,每個模塊都會執(zhí)行一次該類中的方法
* Created by aruba on 2021/11/22.
*/
@AutoService(Processor::class)
//指定要處理的注解
@SupportedAnnotationTypes("com.aruba.librouter_annotation.Router")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
class AnnotationProcessor : AbstractProcessor() {
// 日志打印
private lateinit var MSG: Messager
private fun Messager.print(msg: String) {
printMessage(Diagnostic.Kind.NOTE, msg);
}
// 節(jié)點工具類 (類、函數(shù)、屬性都是節(jié)點) 用來獲取節(jié)點
private lateinit var mElementUtils: Elements
// type(類信息)工具類 用來比對節(jié)點
private lateinit var mTypeUtils: Types
// 文件操作類 用來生成kotlin文件
private lateinit var mFiler: Filer
// Activity類的節(jié)點描述
private val typeActivity by lazy { mElementUtils.getTypeElement(Const.ACTIVITY).asType() }
// IService接口的節(jié)點描述
private val typeIService by lazy { mElementUtils.getTypeElement(Const.ISERVICE).asType() }
// 組名:RouterMeta列表的Map
private val routerMetaByGroup: MutableMap<String, MutableList<RouterMeta>>
by lazy { mutableMapOf() }
// 組名:文件名列表的Map
private val fileNameByGroup: MutableMap<String, String>
by lazy { mutableMapOf() }
private var isInit: Boolean = false
override fun init(processingEnv: ProcessingEnvironment?) {
super.init(processingEnv)
//初始化
processingEnv?.apply {
MSG = messager
MSG.print("init")
mElementUtils = elementUtils
mTypeUtils = typeUtils
mFiler = filer
}
}
/**
* 開始執(zhí)行
*/
override fun process(
typeElementSet: MutableSet<out TypeElement>?,
roundEnvironment: RoundEnvironment?
): Boolean {
if (isInit) return true
isInit = true
typeElementSet?.let {
// 1.獲取所有使用了Router注解的節(jié)點并遍歷
roundEnvironment?.getElementsAnnotatedWith(Router::class.java)?.forEach { element ->
// 2.獲取注解
val router = element.getAnnotation(Router::class.java).apply {
if (path.isEmpty() || group.isEmpty()) {
MSG.print("$element group or path can not be empty")
throw RuntimeException()
}
}
// 3.獲取包裝類
getRouterMeta(element, router, mTypeUtils) {
MSG.print(it.toString())
}.apply {
MSG.print("group:${group} path:${path}")
// 4.加入緩存,即根據(jù)group分組
routerMetaByGroup.getOrPut(group) {
mutableListOf()
}.add(this)
}
}
MSG.print(routerMetaByGroup.toString())
// 5.利用kotlinpoet生成代碼
generateClassByKotlinPoet()
}
return true
}
/**
* 獲取節(jié)點包裝類方法
* 我們定義Router注解只能使用在Activity類和實現(xiàn)了IService接口的類
*/
private fun getRouterMeta(
element: Element,
router: Router,
typeUtils: Types,
mirror: (mirror: TypeMirror) -> Unit = {}
): RouterMeta {
// 獲取當(dāng)前節(jié)點的描述
val type = element.asType().apply {
mirror(this)
}
// 根據(jù)描述是否是typeActivity或者typeIService生成包裝類
return when {
typeUtils.isSubtype(type, typeActivity) -> {//是Activity子類
RouterMeta(path = router.path, group = router.group).apply {
this.element = element
}
}
typeUtils.isSubtype(type, typeIService) -> {//實現(xiàn)了IService接口
RouterMeta(
path = router.path, group = router.group,
type = RouterMeta.Type.ISERVICE
).apply {
this.element = element
}
}
else -> {// 其他情況使用了Router注解,直接拋出異常
MSG.print("$element type:$type not support router")
throw RuntimeException()
}
}
}
/**
* 利用kotlinpoet生成代碼
*/
private fun generateClassByKotlinPoet() {
routerMetaByGroup.forEach { (group, routerMetas) ->
// 分別生成兩個類并實現(xiàn)上面兩個接口,以便在Router中獲取
// 1.先來生成 根據(jù)path集合將RouterMeta包裝類存入一個map 的類
generateRouterPathByKotlinPoet(group, routerMetas)
}
fileNameByGroup.forEach { (group, clzName) ->
// 2.再生成 根據(jù)group將1.生成的類存入一個map 的類
generateRouterGroupByKotlinPoet(group, clzName)
}
}
/**
* 生成RouterPath類
*/
private fun generateRouterPathByKotlinPoet(
group: String,
routerMetas: MutableList<RouterMeta>
) {
// 可以自己先實現(xiàn)一個,再參考著寫
// class RouterPathTest : IRouterPath {
// override fun cacheInRouterMetaByPath(map: LinkedHashMap<String, RouterMeta>) {
// map["path1"] = RouterMeta(
// type = RouterMeta.Type.ACTIVITY,
// path = "path1",
// group = "group1",
// clazz = TestActivity::class.java
// )
// map["path2"] = RouterMeta(
// type = RouterMeta.Type.ACTIVITY,
// path = "path2",
// group = "group1",
// clazz = Test2Activity::class.java
// )
// }
// }
// 1.創(chuàng)建方法,方法名為 cacheInRouterMetaByPath
val funcSpecBuilder = FunSpec.builder(Const.METHOD_IROUTER_PATH)
.addModifiers(KModifier.OVERRIDE)// 方法標(biāo)識override關(guān)鍵字
.addParameter(//添加入?yún)?map: LinkedHashMap<String, RouterMeta>
"map",
LinkedHashMap::class.parameterizedBy(String::class, RouterMeta::class)
)
// 2.為方法添加代碼
// map["path1"] = RouterMeta(
// type = RouterMeta.Type.ACTIVITY,
// path = "path1",
// group = "group1",
// clazz = TestActivity::class.java
// )
routerMetas.forEach { routerMeta ->
funcSpecBuilder.addStatement(
"""
|map[%S] = RouterMeta(
| type = %T.%L,
| path = %S,
| group = %S,
| clazz = %T::class.java
|)
|
""".trimMargin(),
routerMeta.path,//key
RouterMeta.Type::class,//type的類
routerMeta.type,//type的值
routerMeta.path,//path
routerMeta.group,//group
routerMeta.element!!.asType()//clazz
)
}
// 3.創(chuàng)建類
val superInter = ClassName(Const.IROUTER_PACKAGE, Const.IROUTER_PATH_SIMPLENAME)
//生成的文件名 如:Router_module_a_path
val fileName = String.format(Const.FILENAME_FORMAT_PATH, Const.FILENAME_PREFIX, group)
val typeSpec = TypeSpec.classBuilder(fileName)
.addFunction(funcSpecBuilder.build()) // 類中添加方法
.addSuperinterface(superInter) // 實現(xiàn)IRouterPath接口:根據(jù)path集合將RouterMeta存入緩存
.build()
// 4.創(chuàng)建文件
FileSpec.builder(Const.GENERATE_PACKAGE, fileName)//包名,文件名
.addType(typeSpec)
.build()
.writeTo(mFiler) // 寫入文件
MSG.print("創(chuàng)建文件成功:${fileName}")
// 加入緩存中,后續(xù)生成RouterGroup類用
fileNameByGroup[group] = fileName
}
/**
* 生成RouterGroup類
*/
private fun generateRouterGroupByKotlinPoet(group: String, clzName: String) {
// class RouterGroupTest : IRouterGroup {
// override fun cacheInRouterPathByGroup(map: LinkedHashMap<String, Class<out IRouterPath>>) {
// map["group1"] = RouterPathTest::class.java
// }
// }
// 1.創(chuàng)建方法,方法名為 cacheInRouterPathByGroup
val routePathInter = ClassName(Const.IROUTER_PACKAGE, Const.IROUTER_PATH_SIMPLENAME)
val funcSpecBuilder = FunSpec.builder(Const.METHOD_IROUTER_GROUP)
.addModifiers(KModifier.OVERRIDE)// 方法標(biāo)識override關(guān)鍵字
.addParameter(//添加入?yún)?map: LinkedHashMap<String, Class<out IRouterPath>>
"map",
LinkedHashMap::class.java.asClassName().parameterizedBy(
String::class.asTypeName(),//String
Class::class.java.asClassName().parameterizedBy(//Class<out IRouterPath>
WildcardTypeName.producerOf(routePathInter)
)
)
)
// 2.為方法添加代碼 map["group1"] = RouterPathTest::class.java
// 生成的RouterPath類
val generatedRoutePath = ClassName(Const.GENERATE_PACKAGE, clzName)
funcSpecBuilder.addStatement(
"map[%S] = %T::class.java".trimMargin(),
group,
generatedRoutePath
)
// 3.創(chuàng)建類
//生成的文件名 如:Router_module_a_group
val routeGroupInter = ClassName(Const.IROUTER_PACKAGE, Const.IROUTER_GROUP_SIMPLENAME)
val fileName = String.format(Const.FILENAME_FORMAT_GROUP, Const.FILENAME_PREFIX, group)
val typeSpec = TypeSpec.classBuilder(fileName)
.addFunction(funcSpecBuilder.build()) // 類中添加方法
.addSuperinterface(routeGroupInter) // 實現(xiàn)IRouterGroup接口:根據(jù)group將IRouterPath存入緩存
.build()
// 4.創(chuàng)建文件
FileSpec.builder(Const.GENERATE_PACKAGE, fileName)//包名,文件名
.addType(typeSpec)
.build()
.writeTo(mFiler) // 寫入文件
MSG.print("創(chuàng)建文件成功:${fileName}")
}
}
五、Router組件實現(xiàn)
之前只實現(xiàn)了對外的接口,接下來實現(xiàn)真正的跳轉(zhuǎn)功能,編譯期的代碼已經(jīng)生成了,運行時我們需要獲取到它,加載類并利用反射實例化
1.獲取生成類的工具類
/**
* 獲取所有生成的代碼全路徑
* Created by aruba on 2021/11/23.
*/
object ClassUtils {
/**
* 獲得程序所有的apk(instant run會產(chǎn)生很多split apk)
*/
private fun getSourcePaths(context: Context): List<String> {
context.packageManager.getApplicationInfo(
context.packageName, 0
).apply {
val sourcePaths: MutableList<String> = mutableListOf()
sourcePaths.add(sourceDir)
//instant run
splitSourceDirs?.let {
sourcePaths.addAll(it)
}
return sourcePaths
}
}
/**
* 根據(jù)包名獲取路由表
*
* @param context
* @param packageName 包名
* @return
* @throws PackageManager.NameNotFoundException
* @throws IOException
* @throws InterruptedException
*/
fun getFileNameByPackageName(context: Application, packageName: String): Set<String> =
runBlocking {
val classNames: MutableSet<String> = mutableSetOf()
val paths = getSourcePaths(context)
val coroutineScope = CoroutineScope(Dispatchers.IO);
val jobs = mutableListOf<Job>()
for (path in paths) {
coroutineScope.launch {
var dexfile: DexFile? = null
try {
// 加載 apk中的dex 并遍歷 獲得所有包名為 {packageName} 的類
dexfile = DexFile(path)
val dexEntries = dexfile.entries()
while (dexEntries.hasMoreElements()) {
val className = dexEntries.nextElement()
if (className.startsWith(packageName)) {
classNames.add(className)
}
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
if (null != dexfile) {
try {
dexfile.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
}.apply {
jobs.add(this)
}
}
jobs.forEach {
it.join()
}
classNames
}
}
2.Router實現(xiàn)
首先我們獲取到所有IRouterGroup的生成類,類加載并實例化后,調(diào)用方法,存入group-IRouterPath映射關(guān)系Map中
/**
* 初始化
*/
fun init(context: Application) {
// 獲取類
ClassUtils.getFileNameByPackageName(context, GENERATE_PACKAGE)
.filter { className ->
className.endsWith("group")//只獲取RouterGroup
}.forEach { className ->
// 實例化并且調(diào)用方法
(Class.forName(className).getConstructor().newInstance() as IRouterGroup)
.cacheInRouterPathByGroup(CacheMap.RouterPathByGroup)// 加入緩存中
}
}
再實現(xiàn)跳轉(zhuǎn)功能,先從path-RouterMeta映射關(guān)系Map中獲取,如果緩存中沒有,那么利用group-RouterPath映射關(guān)系Map獲取到IRouterPath類,同樣進行類加載并實例化后,調(diào)用方法,存入path-RouterMeta映射關(guān)系Map
/**
* 跳轉(zhuǎn)
*/
fun navigation(context: Context, group: String, path: String) {
// 獲取緩存中的RouterMeta
CacheMap.RouterMetaByPath.getOrPut(path) {
// 緩存中沒有,就利用RouterPath生成類實例化后,調(diào)用cacheInRouterMetaByPath加入
(CacheMap.RouterPathByGroup[group]!!.getConstructor().newInstance() as IRouterPath)
.cacheInRouterMetaByPath(CacheMap.RouterMetaByPath)
CacheMap.RouterMetaByPath[path]!!
}.let { routerMeta ->
context.startActivity(Intent(context, routerMeta.clazz))
}
}
完整ARouter代碼:
/**
* 路由中間件
* Created by aruba on 2021/11/23.
*/
class ARouter private constructor() {
companion object {
val INSTANCE: ARouter by lazy { ARouter() }
// 生成的類的package
private val GENERATE_PACKAGE = "com.aruba.router"
}
/**
* 初始化
*/
fun init(context: Application) {
// 獲取類
ClassUtils.getFileNameByPackageName(context, GENERATE_PACKAGE)
.filter { className ->
className.endsWith("group")//只獲取RouterGroup
}.forEach { className ->
// 實例化并且調(diào)用方法
(Class.forName(className).getConstructor().newInstance() as IRouterGroup)
.cacheInRouterPathByGroup(CacheMap.RouterPathByGroup)// 加入緩存中
}
}
/**
* 跳轉(zhuǎn)
*/
fun navigation(context: Context, group: String, path: String) {
// 獲取緩存中的RouterMeta
CacheMap.RouterMetaByPath.getOrPut(path) {
// 緩存中沒有,就利用RouterPath生成類實例化后,調(diào)用cacheInRouterMetaByPath加入
(CacheMap.RouterPathByGroup[group]!!.getConstructor().newInstance() as IRouterPath)
.cacheInRouterMetaByPath(CacheMap.RouterMetaByPath)
CacheMap.RouterMetaByPath[path]!!
}.let { routerMeta ->
context.startActivity(Intent(context, routerMeta.clazz))
}
}
}
六、測試跳轉(zhuǎn)功能
1.使用Router插件
在libmodule_a中使用Router插件:
dependencies {
api project(path: ':libbase')
implementation project(path: ':librouter_router')
kapt project(':librouter_complier')
}
app中也進行依賴:
dependencies {
implementation project(path: ':libmodule_a')
implementation project(path: ':librouter_router')
kapt project(':librouter_complier')
}
2.使用Router注解
libmodule_a的ModuleAActivity使用注解:
@Router(group = "module_a", path = "ModuleAActivity")
class ModuleAActivity : AppCompatActivity() {
app的MainActivity使用注解:
@Router(group = "app", path = "MainActivity")
class MainActivity : AppCompatActivity() {
3.Application中初始化
class App : Application() {
override fun onCreate() {
super.onCreate()
ARouter.INSTANCE.init(this)
}
}
4.使用Router進行跳轉(zhuǎn)
MainActivity:
@Router(group = "app", path = "MainActivity")
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun toModule(view: android.view.View) {
ARouter.INSTANCE.navigation(this, "module_a", "ModuleAActivity")
}
}
ModuleAActivity:
@Router(group = "module_a", path = "ModuleAActivity")
class ModuleAActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_module_aactivity)
}
fun toMain(view: android.view.View) {
ARouter.INSTANCE.navigation(this, "app", "MainActivity")
}
}
最終效果:

最后細(xì)心的人可能已經(jīng)發(fā)現(xiàn),不同組件之間,group是不能重復(fù)的,一個moudle中可以有多個group
最后附上一張結(jié)構(gòu)圖:
