Kotlin 代码的可空问题
可以看如下的代码:
这里的age 是一个可空的,当你用ksp注解处理器生成代码时就会报错了
可以看下报错信息:
他说这里需要的是一个Int,但是你传的值是一个Int? 那显然这是一个kotlin代码 专属的一个问题了
要解决这个问题其实也不难
用这种方法即可解决:
那剩下的 其实就是 要想办法判断一下 这个变量的类型 到底是不是可空的
好在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 + "]"
)
}
拦截器
之前我们虽然完成了界面跳转以及值传递,但是还漏掉了一个拦截器的机制没有处理,现在补上去
拦截器的实现 应该说很简单了 取一下对应的注解
然后生成一下对应的类即可
这里就不详细解释了, 没什么难点 唯一要注意的就是 注意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的人都不知道咋用,
比如这里 设置了一个extras 的值 为110
我在这里
可以取得这个extra的值, 通常可以利用这个extra 作一些开关的作用
最终生成的代码其实就是RouteMeta这个build 中最后一个参数
用ksp注解处理器的时候 这里不要忘记实现extra, 这里代码就不上了,之前的文章已经介绍过如何实现RouteMeta的build语句了,无非就是取一下对应extra的值而已
到这里 整体上arouter的ksp 的注解处理器就实现完毕了。