ARouter-KSP 注解处理器实现-RouteMeta 传递数据(三)

567 阅读6分钟

AutoWired的 生成逻辑 分析

AutoWired 其实就是Arouter 辅助我们界面跳转时 传值用的, 我们在使用的时候除了要在对应的字段上 加上 AutoWired注解以外, 还需要在对应的界面上 手动调用一下inject方法, 这里我们主要关注AutoWired这个注解使用以后 Arouter 会生成什么样的代码

image.png

生成的这个类,必须实现ISyringe 这个接口

其次 必须初始化一个 serializationService方法

再看一个复杂的类

package com.alibaba.android.arouter.demo.module1.testactivity;

import android.util.Log;
import com.alibaba.android.arouter.demo.service.HelloService;
import com.alibaba.android.arouter.demo.service.model.TestObj;
import com.alibaba.android.arouter.facade.service.SerializationService;
import com.alibaba.android.arouter.facade.template.ISyringe;
import com.alibaba.android.arouter.launcher.ARouter;
import java.lang.Object;
import java.lang.Override;
import java.lang.String;
import java.util.List;
import java.util.Map;

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class Test1Activity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    Test1Activity substitute = (Test1Activity)target;
    substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
    substitute.height = substitute.getIntent().getIntExtra("height", substitute.height);
    substitute.girl = substitute.getIntent().getBooleanExtra("boy", substitute.girl);
    substitute.ch = substitute.getIntent().getCharExtra("ch", substitute.ch);
    substitute.fl = substitute.getIntent().getFloatExtra("fl", substitute.fl);
    substitute.dou = substitute.getIntent().getDoubleExtra("dou", substitute.dou);
    substitute.ser = (com.alibaba.android.arouter.demo.service.model.TestSerializable) substitute.getIntent().getSerializableExtra("ser");
    substitute.pac = substitute.getIntent().getParcelableExtra("pac");
    if (null != serializationService) {
      substitute.obj = serializationService.parseObject(substitute.getIntent().getStringExtra("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    if (null != serializationService) {
      substitute.objList = serializationService.parseObject(substitute.getIntent().getStringExtra("objList"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestObj>>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'objList' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    if (null != serializationService) {
      substitute.map = serializationService.parseObject(substitute.getIntent().getStringExtra("map"), new com.alibaba.android.arouter.facade.model.TypeWrapper<Map<String, List<TestObj>>>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'map' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    substitute.url = substitute.getIntent().getExtras() == null ? substitute.url : substitute.getIntent().getExtras().getString("url", substitute.url);
    substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
  }
}

我们可以观察一下这个类, 看看这个类还有哪些细节

这里 可以关注下面几个重点:

image.png

首先是 ser,和pac, 这2个都是 在编译的时候判断了 到底是 Serializable 还是 Parcelable 接口, 所以在这里实际调用的时候 就会非常简单

但是obj 就不一样了,obj 是没有继承任何接口,对于这种类型的传值,其实就是利用json的序列化了,

否则你是处理不了的,(你自己想想就能明白了,json的序列化 无非就是 利用字符串来传递真正的值)

另外还有一个要注意的是 service,service的处理方式 和普通传值的方式 其实不太一样了

再看一下 RouteMeta 中 对传值的处理

image.png

这里其实十分简单了,

其实就是放了一个map,map的key 就是参数的名字,value 就是参数的类型

参数的类型 就是TypeKind 这个枚举

image.png

基本上 Arouter 传值的逻辑 我们就搞清楚了, 剩下的就是想办法一步步来实现

补充Provider 生成逻辑

为什么在RouteMeta 数据生成之前 我们要补充一下Provider 的逻辑,可以看下 下面ap生成的代码

image.png

这个里面是存储了全部的provider信息,这里最关注的是 prividers 的key,这个key的值本质上是继承自IProvider这个接口的 接口的全路径classname

比如说

HelloService image.png

SerializationService image.png

这个service 要重点提一下,他的作用主要是传递 非序列化值用的,比如你某个值要传递,但是这个类型 他既不是 par接口 也不是ser接口的, 你要在界面之间传递这个值 ,你就只能 利用json的序列化形式, 本质上就是传递字符串,然后字符串拿到以后 利用json的 序列化来解析出原值

Arouter默认的 实现 在

image.png

这个类中

最后再看下singlerservice

image.png

这里主要是看下写法了,这个InnerTest是我自己加上去的,主要是为了后面测试用例使用

总结一下,ARouter 在界面传递值的时候,会利用序列化的service来帮我们对object 类型的值做传递,所以 我们必须实现Privider的路由信息,否则这条路走不通

可以看下Test1Activiyt生成的辅助类信息

image.png

可以看到这些obj的值 都是通过 序列化的service 去实现的

下面就看下怎么生成Provier的 辅助类了, 其实这个辅助类的生成最关键的就是要拿到接口的全名,

假设你有一个类A,他继承了a1,a2,a3 这3个接口, 同时也extends了 b 这个抽象类,你要做的就是 判断一下,a1 a2 a3 哪个是继承的IProvider这个接口, 如果都不是 则要继续看 a1 a2 a3 的父类 哪个继承了IProvider这个接口,然后把这个类的信息 取出来 即可

fun KSClassDeclaration.getProviderInterfaceInfo(logger: KSPLogger?): KSClassDeclaration? {
    val superClass = superTypes.toMutableList()
    // 如果没有super 就返回
    if (superClass.isNullOrEmpty()) {
        return null
    }
    var result: KSClassDeclaration?
    // 这里用循环是因为 一个class 可以同时 extends 一个类,并且implementes 多个 接口,所以你要 foreach去查找下到底哪个才是符合要求的
    superClass.forEach {
        result = this
        var declaration = it.resolve().declaration
        while (declaration is KSClassDeclaration) {
            val name = declaration.qualifiedName?.asString() ?: ""
            if (name == Consts.IPROVIDER) {
                return result
            }
            // 如果还有父类  就继续找下去
            val typeList = declaration.superTypes.toList()
            if (typeList.isNotEmpty()) {
                result = declaration
                declaration = typeList.first().resolve().declaration
            } else {
                break
            }
        }
    }

    return null
}

我们拿到这个ks的信息以后 就简单了,我们完全可以组成一个map

val providerInfoMap = mutableListOf<Map<String, KSClassDeclaration>>()

这个map的key 就是这个ks信息的全路径名,也就是上面生成代码中的key了, value就是使用Route注解并且是IProvider类的 ks类,

有了这2个关键信息以后就很简单了,就是生成loadinto方法

@OptIn(KotlinPoetKspPreview::class)
private fun genLoadIntoFunSpecForProvider(providerInfoMap: MutableList<Map<String, KSClassDeclaration>>): FunSpec {
    // loadinto 方法参数的类型
    val parameterSpec = ParameterSpec.builder(
        "providers",
        MUTABLE_MAP.parameterizedBy(
            String::class.asClassName(),
            RouteMeta::class.asClassName()
        ).copy(nullable = true)
    ).build()
    val funSpec = FunSpec.builder("loadInto").addModifiers(KModifier.OVERRIDE)
        .addParameter(parameterSpec)

    // 增加判空语句
    funSpec.addStatement(
        """
         if(providers==null) {return}
         
         
        """.trimMargin()
    )
    providerInfoMap.forEach {
        it.forEach { (s, ksClassDeclaration) ->
            val routeInfo = ksClassDeclaration.findAnnotationWithType<Route>()
            val path = routeInfo!!.path
            var group = routeInfo.group
            if (group.isEmpty()) {
                group = path.split("/")[1]
            }
            funSpec.addStatement(
                "providers.put("$s", \n" +
                    "        %T.build(%T.PROVIDER, %T::class.java,\n" +
                    "        "$path", "$group", null, -1, -1))",
                RouteMeta::class.asClassName(),
                RouteType::class.asClassName(),
                ksClassDeclaration.toClassName()
            )
        }
    }

    return funSpec.build()
}

这里主要关注下 %T的使用就可以了, 有人一直搞不明白 啥时候用%T,啥时候直接用字符串, 这里做一个小的总结:

1.注解处理器所在的module 里可以引用到的类,那就直接用%T,这种写法 kotlinpoet会自动帮你import 可以省很多事

2.注解处理器所在的module里 无法直接饮引用到的类,比如Log, 就直接字符串写死就可以, 但是这种情况下,一定要记得 addImport, 否则编译会报错

最后看下我们生成的kotlin代码:

image.png

RouteMeta 改进

我们首先想办法对RouteMeta里面的数据 做改进, 就是要把autowired 注解过的信息 以map的方式填进去, 我们可以先思考一下 对应的流程 ,要如何做?

  1. 过滤掉IProvider这个类型的field,因为RouteMeta的map 里面是不放这个类型的数据的,有兴趣的可以仔细看看 arouter生成的代码
  2. 获取到使用autowired注解的全部field
  3. 获取field的类型,这里要注意的是,ksp与之前的ap 获取到的类型值并不相同,我们这里需要做11映射的关系,这里可以看一下 ksp获取到的类型 都是kotlin. 开头的

image.png

  1. 获取真正的name,这一点也很关键,有的autowired注解 还额外使用了 name的信息

image.png

  1. 拿到上面关注的信息以后 就可以建立一个map了 ,然后在构建routemeta的时候 插入到里面 即可

我们首先获得一下Route注解的类中 有哪些字段使用了AutoWired注解

private fun KSClassDeclaration.getAutoWiredListInfo(): List<AutoWiredInfo> {
    val props = getAllProperties().toList()
    val autowiredList = mutableListOf<AutoWiredInfo>()
    props.forEach { prop ->
        // prop的simplename 是属性的名称
        val annitons = prop.findAnnotationWithType<Autowired>()
        if (annitons != null && !prop.isSubclassOf("com.alibaba.android.arouter.facade.template.IProvider")) {
            // 得到key
            var key = prop.simpleName.asString()
            if (!annitons.name.isNullOrEmpty()) {
                key = annitons.name
            }
            // 计算出类型的value
            val value = prop.typeExchange()
            autowiredList.add(AutoWiredInfo(fieldName = key, fieldType = value))
        }
    }
    return autowiredList
}

并且要计算出 这些使用AutoWired注解的field 都是什么类型的

这里要关注的就是Kotlin的类型和原来java的类型 不太一样,Kotlin的类型 值都是Kotlin.开头的

internal fun KSPropertyDeclaration.typeExchange(): Int {
    val type = this.type.resolve()
    return when (type.declaration.qualifiedName?.asString()) {
        Consts.KBYTE -> TypeKind.BYTE.ordinal
        Consts.KSHORT -> TypeKind.SHORT.ordinal
        Consts.KINTEGER -> TypeKind.INT.ordinal
        Consts.KLONG -> TypeKind.LONG.ordinal
        Consts.KFLOAT -> TypeKind.FLOAT.ordinal
        Consts.KDOUBEL -> TypeKind.DOUBLE.ordinal
        Consts.KBOOLEAN -> TypeKind.BOOLEAN.ordinal
        Consts.KCHAR -> TypeKind.CHAR.ordinal
        Consts.KSTRING -> TypeKind.STRING.ordinal
        else -> {
            when (this.isSubclassOf(listOf(Consts.PARCELABLE, Consts.SERIALIZABLE))) {
                0 -> TypeKind.PARCELABLE.ordinal
                1 -> TypeKind.SERIALIZABLE.ordinal
                else -> TypeKind.OBJECT.ordinal
            }
        }
    }
}

拿到这个关键的信息以后,其实你要做的工作就简单了,无非就是把这些信息 拼成你想要的 RouteMeta.build 语句 即可

pathList.forEach { path ->

    val metaInfo = metaMap[group]?.first { info ->
        info.path == path && info.group == group
    }

    // mutableMapOf("ser" to 9, "ch" to 5, "fl" to 6, "dou" to 7, "boy"
    //        to 0, "url" to 8, "pac" to 10, "obj" to 11, "name" to 8, "objList" to 11, "map" to 11, "age"
    //        to 3, "height" to 3, )
    var autowiredInfoString = "null"
    if (!metaInfo?.autowiredInfo.isNullOrEmpty()) {
        val startInfo = StringBuilder("mutableMapOf(")
        metaInfo?.autowiredInfo?.forEach { autowiredItem ->
            startInfo.append(""").append(autowiredItem.fieldName).append("" to ")
                .append(autowiredItem.fieldType)
                .append(", ")
        }
        startInfo.append(")")
        autowiredInfoString = startInfo.toString()
    }

    funSpec.addStatement(
        "atlas.put(%S,  %T.build(%T.${metaInfo!!.type}, %T::class.java, %S, %S, $autowiredInfoString,-1,-1))",
        path,
        RouteMeta::class,
        RouteType::class,
        metaInfo.elementClassName,
        path,
        group
    )
}

看下效果:

image.png

AutoWired辅助类生成

有了前面的基础,最后一步就是生成我们的AutoWired辅助类了,我们先来看看 原来ap生成的autowired辅助类是干啥的,具体是什么结构

首先看actitivity的信息吧

image.png

image.png

这里本质上还是做了赋值,要关注如下几点

  1. 基本类型的数据获取 是最简单的,都是固定套路
  2. 注意autowired别名的时候的写法, 例如截图中的substitue.girl
  3. 对象类型的写法较为复杂,主要是 ser和pac类型,这2个比较简单,但是还有最复杂的obj类型
  4. obj类型的解析 本质上还是靠的前面provider中的 序列化service来完成的,
  5. 注意看helloservice的写法,这个是最简单的 固定写法 甚至都不需要判断类型
  6. 其实这里代码你也能看出来,为啥arouter中 使用autowired的变量不能是privtate

再看下fragment

image.png

这个fragment 其实和activity 大体上没啥差别,唯一的不同就在于这里取参数是getArguments这个函数

我们在生成代码的时候 这里要做一些activity和fragment的区分,避免出错

首先可以声明一个辅助类,fieldName就是 使用autowired的 字段名称, ksPropertyDeclaration 就是

这个字段本身的 ksp描述,

data class AutoWiredItem(val fieldName: String, val ksPropertyDeclaration: KSPropertyDeclaration)

然后扫描一遍 取得我们想要的信息

val symbol = resolver.getSymbolsWithAnnotation(Autowired::class.qualifiedName!!)
val elements = symbol.filterIsInstance<KSPropertyDeclaration>().toList()

// key 类名,value 是这个类下面 使用autowired的 fiedld 信息
val classMap = mutableMapOf<String, KSClassDeclaration>()
// key 是类名, value是 这个类下面 所有autowired注解的信息
val paramMapInfo = mutableMapOf<String, MutableList<AutoWiredItem>>()

elements.forEach { ksproperty ->

    (ksproperty.parentDeclaration as? KSClassDeclaration)?.let {
        val className = it.qualifiedName?.asString()!!
        classMap[className] = it

        var fieldName = ksproperty.simpleName.asString()
        val info = AutoWiredItem(
            fieldName = fieldName,
            ksPropertyDeclaration = ksproperty
        )
        if (paramMapInfo[className] != null) {
            paramMapInfo[className]!!.add(info)
        } else {
            paramMapInfo[className] = mutableListOf(info)
        }
    }
}

有了这些信息 就足够我们生成的对应的代码了,

这里无非就是注意几点变化:

  1. fragment 和acitivity的 取参的写法不同
  2. IProvider的数据要单独处理
  3. obj类型的数据 单独处理
classMap.forEach { (s, ksClassDeclaration) ->

    val className = "${ksClassDeclaration.simpleName.asString()}$$ARouter$$Autowired"

    val funSpec = FunSpec.builder("inject").addModifiers(KModifier.OVERRIDE)
        .addParameter(parameterSpec)

    //  serializationService = ARouter.getInstance().navigation(SerializationService::class.java)
    funSpec.addStatement(
        "serializationService = %T.getInstance().navigation(%T::class.java)",
        ClassName("com.alibaba.android.arouter.launcher", "ARouter"),
        JSON_SERVICE_CLASS_NAME
    )

    // val substitute = (target as? BaseActivity)?: throw IllegalStateException(
    //         """The target that needs to be injected must be BaseActivity, please check your code!"""
    //         )
    val parentClassName = ksClassDeclaration.toClassName()
    funSpec.addStatement(
        "val substitute = (target as? %T)?: throw IllegalStateException(\n·"""The target that needs to be injected must be %T, please check your code!"""\n·)",
        parentClassName,
        parentClassName
    )

    // 处理 activity和fragment
    val autowiredInfoList = paramMapInfo[s]
    // for fragment
    var params = "arguments"
    var paramsObject = "arguments"
    var getParceValue = "getParcelable"
    var getSeValue = "getSerializable"
    if (ksClassDeclaration.isSubclassOf(
            listOf(
                    Consts.ACTIVITY,
                    "androidx.appcompat.app.AppCompatActivity",
                    "androidx.core.app.ComponentActivity",
                    "androidx.fragment.app.FragmentActivity"
                ),
            null
        ) > -1
    ) {
        params = "intent?.extras"
        paramsObject = "intent"
        getParceValue = "getParcelableExtra"
        getSeValue = "getSerializableExtra"
    }
    autowiredInfoList?.forEach { autowiredItem ->

        // 如果是provider类型
        if (autowiredItem.ksPropertyDeclaration.isSubclassOf(listOf(Consts.IPROVIDER)) > -1) {
            funSpec.addStatement(
                "substitute.${autowiredItem.fieldName} = %T.getInstance().navigation(%T::class.java)",
                AROUTER_CLASS_NAME,
                autowiredItem.ksPropertyDeclaration.getKotlinPoetTTypeGeneric()
            )
            return@forEach
        }

        val annotation = autowiredItem.ksPropertyDeclaration.findAnnotationWithType<Autowired>()
        var getKeyInfo = autowiredItem.fieldName
        if (!annotation?.name.isNullOrEmpty()) {
            getKeyInfo = annotation!!.name
        }

        var typeState = ""
        when (autowiredItem.ksPropertyDeclaration.typeExchange()) {
            TypeKind.BYTE.ordinal -> {
                typeState = "it.getByte"
            }
            TypeKind.BOOLEAN.ordinal -> {
                typeState = "it.getBoolean"
            }
            TypeKind.SHORT.ordinal -> {
                typeState = "it.getShort"
            }
            TypeKind.INT.ordinal -> {
                typeState = "it.getInt"
            }
            TypeKind.LONG.ordinal -> {
                typeState = "it.getLong"
            }
            TypeKind.CHAR.ordinal -> {
                typeState = "it.getChar"
            }
            TypeKind.FLOAT.ordinal -> {
                typeState = "it.getFloat"
            }
            TypeKind.DOUBLE.ordinal -> {
                typeState = "it.getDouble"
            }
            TypeKind.STRING.ordinal -> {
                typeState = "it.getString"
            }
            TypeKind.SERIALIZABLE.ordinal -> {
                val statement = "(substitute.$paramsObject?.$getSeValue("$getKeyInfo") as? %T)?.let {\n" +
                    "      substitute.${autowiredItem.fieldName} = it\n" +
                    "    }"
                funSpec.addStatement(statement, autowiredItem.ksPropertyDeclaration.getKotlinPoetTTypeGeneric())
                return@forEach
            }
            TypeKind.PARCELABLE.ordinal -> {
                val statement = "substitute.$paramsObject?.$getParceValue<%T>" +
                    "("$getKeyInfo")?.let { \n " +
                    "      substitute.${autowiredItem.fieldName} = it\n" +
                    "    } \n"
                funSpec.addStatement(statement, autowiredItem.ksPropertyDeclaration.getKotlinPoetTTypeGeneric())
                return@forEach
            }
            TypeKind.OBJECT.ordinal -> {
                val statement = " if(serializationService != null && substitute.$params != null) {\n" +
                    "      substitute.${autowiredItem.fieldName} =\n" +
                    "          serializationService!!.parseObject(substitute.$params!!.getString("$getKeyInfo"), (object :\n" +
                    "          %T<%T>(){}).type)\n" +
                    "    } else {\n" +
                    "      Log.e("ARouter::" , """You want automatic inject the field '${autowiredItem.fieldName}' in class '${ksClassDeclaration.simpleName.asString()}',\n" +
                    "          then you should implement 'SerializationService' to support object auto inject!""" )\n" +
                    "    }"
                funSpec.addStatement(statement, TypeWrapper::class.java, autowiredItem.ksPropertyDeclaration.getKotlinPoetTTypeGeneric())
            }
            else -> {
            }
        }

        if (typeState.isNotEmpty()) {
            var statement = "substitute.$params?.let {\n" +
                "      substitute.${autowiredItem.fieldName} = $typeState("$getKeyInfo", substitute.${autowiredItem.fieldName})\n" +
                "    }"
            funSpec.addStatement(statement)
        }
    }

    val packageName = s.substring(0, s.lastIndexOf("."))

    val file = FileSpec.builder(packageName, className)
        .addImport("android.util", "Log") //
        .addType(
            TypeSpec.classBuilder(className).addSuperinterface(
                ClassName(
                    "com.alibaba.android.arouter.facade.template",
                    "ISyringe"
                )
            ).addFunction(funSpec.build()).addProperty(serializationServiceProperty).build()
        )
        .build()

    file.writeTo(codeGen, false)
}

最后看下生成的kotlin代码

package com.alibaba.android.arouter.demo.module1.testactivity

import android.util.Log
import com.alibaba.android.arouter.demo.service.HelloService
import com.alibaba.android.arouter.demo.service.model.TestObj
import com.alibaba.android.arouter.demo.service.model.TestParcelable
import com.alibaba.android.arouter.demo.service.model.TestSerializable
import com.alibaba.android.arouter.facade.model.TypeWrapper
import com.alibaba.android.arouter.facade.service.SerializationService
import com.alibaba.android.arouter.facade.template.ISyringe
import com.alibaba.android.arouter.launcher.ARouter
import kotlin.Any
import kotlin.String
import kotlin.Unit
import kotlin.collections.MutableList
import kotlin.collections.MutableMap

public class `Test1Activity$$ARouter$$Autowired` : ISyringe {
  private var serializationService: SerializationService? = null

  public override fun inject(target: Any?): Unit {
    serializationService = ARouter.getInstance().navigation(SerializationService::class.java)
    val substitute = (target as? Test1Activity)?: throw IllegalStateException(
         """The target that needs to be injected must be Test1Activity, please check your code!"""
         )
    substitute.intent?.extras?.let {
              substitute.age = it.getInt("age", substitute.age)
            }
    substitute.intent?.extras?.let {
              substitute.height = it.getInt("height", substitute.height)
            }
    substitute.intent?.extras?.let {
              substitute.girl = it.getBoolean("boy", substitute.girl)
            }
    substitute.intent?.extras?.let {
              substitute.ch = it.getChar("ch", substitute.ch)
            }
    substitute.intent?.extras?.let {
              substitute.fl = it.getFloat("fl", substitute.fl)
            }
    substitute.intent?.extras?.let {
              substitute.dou = it.getDouble("dou", substitute.dou)
            }
    (substitute.intent?.getSerializableExtra("ser") as? TestSerializable)?.let {
              substitute.ser = it
            }
    substitute.intent?.getParcelableExtra<TestParcelable>("pac")?.let { 
               substitute.pac = it
            } 

     if(serializationService != null && substitute.intent?.extras != null) {
              substitute.obj =
                  serializationService!!.parseObject(substitute.intent?.extras!!.getString("obj"),
            (object :
                  TypeWrapper<TestObj>(){}).type)
            } else {
              Log.e("ARouter::" , """You want automatic inject the field 'obj' in class
            'Test1Activity',
                  then you should implement 'SerializationService' to support object auto inject!"""
            )
            }
     if(serializationService != null && substitute.intent?.extras != null) {
              substitute.objList =
                 
            serializationService!!.parseObject(substitute.intent?.extras!!.getString("objList"),
            (object :
                  TypeWrapper<MutableList<TestObj>>(){}).type)
            } else {
              Log.e("ARouter::" , """You want automatic inject the field 'objList' in class
            'Test1Activity',
                  then you should implement 'SerializationService' to support object auto inject!"""
            )
            }
     if(serializationService != null && substitute.intent?.extras != null) {
              substitute.map =
                  serializationService!!.parseObject(substitute.intent?.extras!!.getString("map"),
            (object :
                  TypeWrapper<MutableMap<String, MutableList<TestObj>>>(){}).type)
            } else {
              Log.e("ARouter::" , """You want automatic inject the field 'map' in class
            'Test1Activity',
                  then you should implement 'SerializationService' to support object auto inject!"""
            )
            }
    substitute.intent?.extras?.let {
              substitute.url = it.getString("url", substitute.url)
            }
    substitute.helloService = ARouter.getInstance().navigation(HelloService::class.java)
  }
}

最后跑一下arouter的测试用例 完美!