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

682 阅读2分钟

Kotlin 代码的可空问题

可以看如下的代码:

image.png

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

可以看下报错信息: image.png

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

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

用这种方法即可解决:

image.png

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

好在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 + "]"
    )
}

拦截器

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

拦截器的实现 应该说很简单了 取一下对应的注解 image.png

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

image.png

这里就不详细解释了, 没什么难点 唯一要注意的就是 注意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的人都不知道咋用,

image.png

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

我在这里

image.png

可以取得这个extra的值, 通常可以利用这个extra 作一些开关的作用

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

image.png

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

到这里 整体上arouter的ksp 的注解处理器就实现完毕了。