Kotlin 代码的可空问题

能够看如下的代码:

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

这儿的age 是一个可空的,当你用ksp注解处理器生成代码时就会报错了

能够看下报错信息:

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

他说这儿需要的是一个Int,可是你传的值是一个Int? 那明显这是一个kotlin代码 专属的一个问题了

要解决这个问题其实也不难

用这种办法即可解决:

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

那剩余的 其实便是 要想办法判别一下 这个变量的类型 到底是不是可空的

好在ksp中的特点 有办法能够直接拿到是否为空的符号

KSPropertyDeclaration.type.resolve().isMarkedNullable

有了这个其实就很简略了,无非便是判别一下 假如可空,那就换一下写法,仅此而已。 代码就不上了

添加private校验

这个其实便是因为Arouter 的机制 原因,运用autowried注解的变量 有必要对错private的

所以假如你断定了对错private的 记得让编译提示 有用的信息 便利开发者

if (ksproperty.modifiers.contains(Modifier.PRIVATE)) {
    throw IllegalAccessException(
        "The inject fields CAN NOT BE 'private'!!! please check field [" +
            ksproperty.simpleName.asString() + "] in class [" + className + "]"
    )
}

拦截器

之前咱们尽管完成了界面跳转以及值传递,可是还漏掉了一个拦截器的机制没有处理,现在补上去

拦截器的完成 应该说很简略了 取一下对应的注解

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

然后生成一下对应的类即可

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

这儿就不具体解释了, 没什么难点 仅有要留意的便是 留意loadInto参数的生成

@OptIn(KotlinPoetKspPreview::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
    // 这儿是取module name 一般情况下module name的方式都是
    // A-B, 这儿就过滤掉特殊符号,转成AB 即可
    var moduleName = options[Consts.KEY_MODULE_NAME]
    if (moduleName.isNullOrEmpty()) {
        logger.warn(" u must set ${KConstants.kspArgHint}")
    } else {
        moduleName = moduleName.replace("[^0-9a-zA-Z_]+".toRegex(), "")
    }
    val symbol = resolver.getSymbolsWithAnnotation(Interceptor::class.qualifiedName!!)
    val elements = symbol.filterIsInstance<KSClassDeclaration>().toList()
    val map = mutableMapOf<Int, KSClassDeclaration>()
    elements.forEach { ks ->
        val annotation = ks.findAnnotationWithType<Interceptor>()
        val key = annotation!!.priority
        map[key] = ks
    }
    // 假如没有 那就直接返回 不要走剩余的流程了
    if (map.isNullOrEmpty()) {
        return emptyList()
    }
    // 仅有要留意的便是这儿了 留意参数的类型构造
    val parameterSpec = ParameterSpec.builder(
        "interceptor",
        MUTABLE_MAP.parameterizedBy(
            INT,
            Class::class.asClassName().parameterizedBy(
                WildcardTypeName.producerOf(
                    Consts.IINTERCEPTOR.quantifyNameToClassName()
                )
            )
        ).copy(nullable = true)
    ).build()
    val className = "ARouter$$Interceptors$$$moduleName"
    val funSpec = FunSpec.builder("loadInto").addModifiers(KModifier.OVERRIDE)
        .addParameter(parameterSpec)
    funSpec.addStatement("if(interceptor == null) { return }")
    map.forEach { (key, ksClassDeclaration) ->
        // interceptor.put(7, Test1Interceptor::class.java)
        funSpec.addStatement(
            "interceptor.put($key, %T::class.java)",
            ksClassDeclaration.toClassName()
        )
    }
    val file = FileSpec.builder("com.alibaba.android.arouter.routes", className)
        .addType(
            TypeSpec.classBuilder(className).addSuperinterface(
                ClassName(
                    "com.alibaba.android.arouter.facade.template",
                    "IInterceptorGroup"
                )
            ).addFunction(funSpec.build()).build()
        )
        .build()
    file.writeTo(codeGen, false)
    return emptyList()
}

extra

这个功用其实很多用Arouter的人都不知道咋用,

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

比如这儿 设置了一个extras 的值 为110

我在这儿

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

能够获得这个extra的值, 通常能够利用这个extra 作一些开关的效果

最终生成的代码其实便是RouteMeta这个build 中最终一个参数

ARouter-KSP 注解处理器实现- Corner Case 问题修复(四)

用ksp注解处理器的时候 这儿不要忘记完成extra, 这儿代码就不上了,之前的文章已经介绍过如何完成RouteMeta的build语句了,无非便是取一下对应extra的值而已

到这儿 整体上arouter的ksp 的注解处理器就完成结束了。