手记

KRouter 1.0 发布,支持参数注入和跨平台开发

因为时间紧张,KRouter 的第一个版本是在仓促中开发出来的,功能勉强可用而已。最近我有空闲时间重新设计和优化了它,增加了对 Kotlin Multiplatform 和参数注入的支持的功能。实现也被彻底重构了,用 KSP 替换了 ServiceLoader 来收集路由信息。它已经发布到了 Maven Central Repository。

GitHub - 0xZhangKe/KRouter: 轻量且简单的 Kotlin 路由器,基于 ServiceLoader 和 KSP 的。用于 Kotlin 模块间通信。……github.com

作为一个路由框架,它不仅要能够收集路由,还要能将收集到的路由信息提供给路由系统。这就是我为什么在第一个版本中使用了 ServiceLoader 的原因。所以在开发这个新版本时,我最初计划使用 KCP 修改字节码来实现这一功能。经过几天的研究,我发现这种方法还是不行。最后卡在了 KCP 无法读写依赖模块的类文件的问题上。我现在知道的是,KCP 只能读写当前模块的文件,不能读写依赖模块的类文件。KSP 也通过一些复杂的方式实现了类似的功能,相关的文档内容贫乏,不够详细,所以我决定放弃。如果有其他解决方法或建议,请不吝告知。

怎么用:

依然简单明了,与以前一样。

首先标注目标类:

@Destination("screen/main")  
class 主屏幕(@RouteParam("id") val id: String, @RouteParam("name") val name: String): 屏幕

然后,用 KRouter 查找相应的类。

val screen = KRouter.route<Screen>("screen/main?name=zhangke&id=123")
// 屏幕的路由配置

KRouter 目前支持三种注解标签:@Destination@RouteUri,和 @RouterParam

到的地方

正如其名,@Destination 注解标记了路由的目标类,也就是目的地,带有一个参数,表示路由目标地址。

    @目标(AnnotationTarget.CLASS::class)  
    @保留(AnnotationRetention.RUNTIME::class)  
    annotation class Destination(val route: String)

它可以这样用:

    @Destination("屏幕/资料详情/详情")  
    class ProfileDetailScreen 屏幕类
RouterParam

@RouterParam 注解用来标记路由中的参数。被这个注解标记的字段将会自动填充对应的路由参数值。

它还接收一个参数,表示路由中查询字段的名称。KRouter 会根据该字段名解析并赋值。

    // 路由参数注解,用于标记路由参数名称
    @Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)  
    @Retention(AnnotationRetention.RUNTIME)  
    annotation class RouteParam(val name: String)

这里有一个使用它的例子:

@Destination("screen/home/detail")  
class HomeDetailScreen(  
    @RouteParam("id") val id: String,  
) : Screen {  
    // 具有id参数的家庭详情屏幕
}

除了支持构造函数参数注入外,还支持属性注入。

    @Destination("screen/home/detail")  
    class HomeDetailScreen(  
        @RouteParam("id") val id: String,  // 页面标识id
    ) : Screen {  
        @RouteParam("title") var title: String? = null  // 标题
    }

目前,参数类型目前仅限于基本类型和 String。对于更复杂的类型,你可以将对象转换为 JSON 字符串,然后将其编码进路由。

请留意,KSP 当前无法获取参数和属性的默认值。因此,注入字段不能设置默认值。这意味着,如果你设置了注入字段的默认值,而路由中没有包含这个参数,那么这个默认值就会被忽略。

技术术语:路由URI

带有此注解的参数或属性将被赋值为完整的路由路径。由于@RouterParam仅适用于字段注入,对于更复杂的解析情况,可以使用@RouteUri来获取完整的路由路径。

KRouterModule模块

KRouterModule 是一个用于实现特定路由功能的接口,它。你可以通过 KRouter 类动态添加自定义模块。默认情况下,它会优先使用动态添加的模块;如果路由失败,则会退回到 KRouter 自带的路由。

接口 KRouterModule {  
   fun 路由(uri: String): Any?  // 路由到指定的URI并返回任意类型的结果
}
添加依赖

KRouter 提供了两个 KSP 插件。

  • krouter-collecting-compiler: 收集路由相关的数据,用于非主模块的。
  • krouter-reducing-compiler: 汇总来自各个模块的路由信息,仅用于主模块(如应用程序模块)。
    // 在非主模块中使用  
    ksp("io.github.0xzhangke:krouter-collecting-compiler:$latest_version")  
    // 在主模块中使用  
    ksp("io.github.0xzhangke:krouter-reducing-compiler:$latest_version")

此外,还有一个注解模块和运行时组件。

  • krouter-runtime: 运行时模块,提供 KRouterKRouterModule 注解及相关的注解支持。

  • krouter-annotation: 注解模块,仅包含注解,没有提供任何类。
    // 仅需使用注解的模块  
    implementation("io.github.0xzhangke:krouter-runtime:$latest_version")  
    // 需要路由功能的模块可以直接  
    implementation("io.github.0xzhangke:krouter-annotation:$latest_version")
实现细节

首先,collecting-compiler 插件(编译器)收集模块内所有路由目标类的信息,并生成一个属于当前模块的 KRouterModule。生成的类可能如下:

    public class RouterCollection_1726153189290() : KRouterModule {  
        override fun route(uri: String): Any? {  
            val routerUri = com.zhangke.krouter.internal.KRouterUri.create(uri)  
            return when (routerUri.baseUrl) {  
                "screen/home/detail" -> {  
                    com.zhangke.krouter.sample.home.HomeDetailScreen(  
                        id = routerUri.requireQuery("id"),  
                    )  
                }  
                "screen/home/landing" -> {  
                    com.zhangke.krouter.sample.home.HomeLandingScreen(  
                        router = uri,  
                    )  
                }  
                else -> null  
            }  
        }  
    }

所有依赖于 collecting-compiler 插件的模块,都会生成这样一个类。

然后,在主项目模块(通常是 app 模块),需要引入 reducing-compiler 插件。此插件生成一个固定包名和类名的类,收集 collecting-compiler 生成的全部类,并将这些类添加至新类中。

    public class AutoReducingModule() : KRouterModule {  

      private val moduleList: List<KRouterModule> = listOf<KRouterModule>(  
              com.zhangke.krouter.generated.RouterCollection_1726153189283(),  
              com.zhangke.krouter.generated.RouterCollection_1726153189290(),  
              com.zhangke.krouter.generated.RouterCollection_1726153189284(),  
              com.zhangke.krouter.generated.RouterCollection_1726153189709()  
          )  

      override fun route(uri: String): Any? = moduleList.firstNotNullOfOrNull { it.route(uri) }  
    }

运行时,KRouter 通过反射创建该类,并将路由处理交给该类中的实现来处理。这样就完成了整个路由的收集和实现。

0人推荐
随时随地看视频
慕课网APP