Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
0xZhangKe authored Sep 19, 2024
1 parent fcba8ea commit 5c112ca
Showing 1 changed file with 129 additions and 72 deletions.
201 changes: 129 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,111 +1,168 @@
[![Release](https://jitpack.io/v/0xZhangKe/KRouter.svg)](https://jitpack.io/#0xZhangKe/KRouter)
![](https://img.shields.io/maven-central/v/io.github.0xzhangke/krouter-runtime)

# KRouter
Find interface implementation by uri string.
# KRouter2

For Kotlin module to module communication.
Due to time constraints, the first version of KRouter was hastily developed and only barely functional. Recently, I had some free time to redesign and refine its usage, adding support for Kotlin Multiplatform and parameter injection. The implementation was also completely revamped, replacing ServiceLoader with KSP for collecting route information. Additionally, it has been published to the Maven Central Repository.

The main purpose is to open the compose screen from other modules(e.g. [Voyager](https://voyager.adriel.cafe/navigation)).
## How to Use

Base on ServiceLoader, KSP and Kotlin reflect.
The usage remains as simple and straightforward as before. First, annotate the target class:

# Usage
Firstly, define a interface.
```kotlin
interface Screen
@Destination("screen/main")
class MainScreen(
@RouteParam("id") val id: String,
@RouteParam("name") val name: String,
): Screen
```
After that, define some implementation.

Then, use `KRouter` to retrieve the corresponding class:

```kotlin
@Destination("screen/home")
class HomeScreen(@Router val router: String = "") : Screen
val screen = KRouter.route<Screen>("screen/main?name=zhangke&id=123")
```

@Destination("screen/profile")
class ProfileScreen : Screen {
@Router
lateinit var router: String
}
KRouter currently provides three annotations: `@Destination`, `@RouteUri`, and `@RouterParam`.

### @Destination

As the name suggests, the `@Destination` annotation marks the target class of a route, i.e., the destination, and takes a parameter as the route address.

```kotlin
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Destination(val route: String)
```
This implementation can be distributed to any modules.

Note that Destination class must have a No-Arg constructor.
It can be used as follows:

Now, you can route to any Screen by router.
```kotlin
val homeScreen = KRouter.route<Screen>("screen/home?name=zhangke")
val profileScreen = KRouter.route<Screen>("screen/profile?name=zhangke")
@Destination("screen/profile/detail")
class ProfileDetailScreen: Screen
```
As show above, you will get homeScreen, and it's router property is `screen/home?name=zhangke`.

See the [sample.app](https://github.com/0xZhangKe/KRouter/tree/main/sample/app/src/main/java/com/zhangke/kouter/sample/app) module for a more detailed example.
### @RouterParam

## Integration
Firstly, you must set up [KSP](https://kotlinlang.org/docs/ksp-overview.html) in your project.
The `@RouterParam` annotation is used to mark route parameters. The fields marked by this annotation will be automatically injected with the corresponding route parameters. It also takes a parameter that represents the name of the query field in the route. KRouter will parse and assign values based on this field name.

Then, add KRouter dependency in module.
```kts
// module's build.gradle.kts
implementation("com.github.0xZhangKe.KRouter:core:0.1.6")
ksp("com.github.0xZhangKe.KRouter:compiler:0.1.6")
```kotlin
@Target(AnnotationTarget.PROPERTY, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class RouteParam(val name: String)
```
Additionally, add resources dir in source sets.
```kts
// module's build.gradle.kts
kotlin {
sourceSets.main {
resources.srcDir("build/generated/ksp/main/resources")
}

Here’s an example of how to use it:

```kotlin
@Destination("screen/home/detail")
class HomeDetailScreen(
@RouteParam("id") val id: String,
) : Screen {
}
```

In addition to constructor parameter injection, property field injection is also supported.

```kotlin
@Destination("screen/home/detail")
class HomeDetailScreen(
@RouteParam("id") val id: String,
) : Screen {

@RouteParam("title") var title: String? = null
}
```

## @Destination
Destination annotation is defined for a route Destination.
Currently, parameter types are limited to basic types and `String`. For more complex types, you can convert the object to a JSON string and encode it into the route.

It's having two parameters:
- `route`: This destination's identify route, must be uri string.
- `type`: Which interface or abstract class this destination for, ignore this if just have single super type.
<aside>
💡

## @Router
This annotation is used to identify which property is used to accept the route.
Please note that KSP cannot access default values for parameters and properties at the moment. Therefore, injected fields cannot have default values. This means that if your injected field has a default value, and the route does not include that parameter, the default value will be ignored.

So, this property must be a class's variable property or constructor parameter.
</aside>

The router is passed into this field when the destination object is constructed.
### @RouteUri

# Release
Parameters or properties injected with this annotation will be assigned the full route. Since `@RouterParam` can only be used for field injection, for more complex parsing scenarios, you can use `@RouteUri` to retrieve the full route.

# 0.2.3
- fixed increase compiler error
### KRouterModule

## 0.2.1
- upgrade to kotlin 1.9.22
`KRouterModule` is an interface used to implement specific routing capabilities. You can dynamically add custom modules through the `KRouter` class. By default, it will use the dynamically added modules first; if routing fails, it will fall back to KRouter's internal routing.

Maybe you need add this code in your module:
```kts
tasks.withType<ProcessResources>{
duplicatesStrategy = DuplicatesStrategy.INCLUDE
```kotlin
interface KRouterModule {
fun route(uri: String): Any?
}
```

## 0.1.6
- Supported multi router parameter in Destination
- Supported kotlin increase compiler
# 0.1.5
Firstly version.
### Adding Dependencies

KRouter provides two KSP plugins:

# License
- `krouter-collecting-compiler`: Collects route information, used in non-main modules.
- `krouter-reducing-compiler`: Aggregates route information from various modules, only used in the main project module (app module).

```kotlin
// Used in non-main modules
ksp("io.github.0xzhangke:krouter-collecting-compiler:$latest_version")
// Used in the main module
ksp("io.github.0xzhangke:krouter-reducing-compiler:$latest_version")
```

Additionally, there is an annotation module and a runtime module:

- `krouter-runtime`: The runtime module, providing `KRouter`, `KRouterModule`, and annotations.
- `krouter-annotation`: The annotation module, containing only the annotations.

```kotlin
// For modules that only need to use annotations
ksp("io.github.0xzhangke:krouter-runtime:$latest_version")
// For modules that need routing capabilities
ksp("io.github.0xzhangke:krouter-annotation:$latest_version")
```
Copyright 2023 ZhangKe Contributors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
## Implementation Details

https://www.apache.org/licenses/LICENSE-2.0
First, the `collecting-compiler` plugin collects all route target class information in the module and generates a `KRouterModule` belonging to the current module. The generated class might look like this:

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```kotlin
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
}
}
}
```

All modules that depend on the `collecting-compiler` plugin will generate a class like this.

Then, in the main project module (typically the app module), the `reducing-compiler` plugin is needed. This plugin generates a class with a fixed package name and class name, gathers all the classes generated by `collecting-compiler`, and adds them to the new class.

```kotlin
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) }
}
```

At runtime, KRouter uses reflection to create this class, and the routing process is delegated to the implementation in this class. This completes the entire process of route collection and implementation.

0 comments on commit 5c112ca

Please sign in to comment.