- Plugin One-Click Migration (ARouter ---> Router)
- Configuration
- Detailed Usage
- Advanced Usage
- ARouter migration to Router
Use KSP technology to implement routing related functions, mainly to improve compilation performance and solve the compatibility problem between ARouter and KSP. The functions of this project mainly refer to Alibaba's ARouter. I hope Router can bring you a better experience in the project
1. KSP feature advantages: For details, please refer to the official document, which will not be explained in detail here
9. Support instantiation of target classes, multi-module decoupling (instantiation of subclasses of Fragment and ProvideService)
plugins{
id "com.google.devtools.ksp" version "2.0.10-1.0.24"
id "org.jetbrains.kotlin.jvm" version "2.0.10"
}//ksp plugin
plugins {
id 'com.google.devtools.ksp'
}
android{
//JAVA and jvm are set to 11 or higher
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = '11'
}
}
dependencies {
implementation('io.github.hearthappy:router-core:1.0.2')
ksp('io.github.hearthappy:router-compiler:1.0.2')
}@Route(path ="/launcher/main")
class MainActivity : BaseActivity<ActivityMainBinding>(){/*...*/}
@Route("/model/fragment")
class RouterFragment : AbsBaseFragment<FragmentRouterBinding>() {/*...*/}
@Route("/service/backend")
class RouterService : Service() {/*...*/}//Path jumps to Activity
Router.build("/launcher/main").navigation()
//uri jump to Activity
val uri = Uri.parse("hearthappy://kotlin.ksp.com/model2/ui?name=Uri jump to Modules2Activity &age=18")
Router.build(uri).navigation()
//Start the service
Router.build("/service/backend").navigation()Router.build("/case/inject")
.withObject("withObject", UserBean("Labubu", "987654"))
.withString("withString", "Labubu")
.withInt("withInt", 123456)
.withBoolean("withBoolean", true)
.withShort("withShort", 1234)
.withLong("withLong", 1234567890)
.withFloat("withFloat", 123.456f)
.withDouble("withDouble", 123.4567890)
.withByte("withByte", 123)
.withChar("withChar", 'b')
.withCharSequence("withCharSequence", "from charSequence")
.withParcelable("withParcelable", ParcelableBean("Labubu", "123456"))
.withSerializable("withSerializable", LoginBean("Labubu", "123456"))
.withBundle("withBundle", Bundle().apply { putString("withBundle", "bundle data") })
.withIntArray("withIntArray", intArrayOf(1, 2, 3))
.withByteArray("withByteArray", byteArrayOf(1, 2, 3))
.withShortArray("withShortArray", shortArrayOf(1, 2, 3))
.withCharArray("withCharArray", charArrayOf('a', 'b', 'c'))
.withFloatArray("withFloatArray", floatArrayOf(1f, 2f, 3f))
.withDoubleArray("withDoubleArray", doubleArrayOf(1.0, 2.0, 3.0))
.withBooleanArray("withBooleanArray", booleanArrayOf(true, false))
.withStringArray("withStringArray", arrayOf("a", "b", "c"))
.withLongArray("withLongArray", longArrayOf(1, 2, 3))
.withCharSequenceArray("withCharSequenceArray", arrayOf("a", "b", "c"))
.withParcelableArray("withParcelableArray", arrayOf(ParcelableBean("Labubu", "123456"), ParcelableBean("Alibaba", "123456")))
.withSparseParcelableArray("withSparseParcelableArray", SparseArray<Parcelable>().apply { put(0, ParcelableBean("Labubu", "123456")) })
.withParcelableArrayList("withParcelableArrayList", arrayListOf(ParcelableBean("Labubu", "123456"), ParcelableBean("Alibaba", "123456")))
.withIntArrayList("withIntArrayList", arrayListOf(1, 2, 3))
.withStringArrayList("withStringArrayList", arrayListOf("a", "b", "c"))
.withCharSequenceArrayList("withCharSequenceArrayList", arrayListOf("a", "b", "c"))
.withTransition(R.anim.window_bottom_in, R.anim.window_bottom_out)
.withOptionsCompat(ActivityOptionsCompat.makeScaleUpAnimation(view, view.width / 2, view.height / 2, view.width, view.height))
.navigation()4. Target page injection parameters (supports lateinit, Delegates and other delay or delegation functions, automatic injection will initialize the default values)
import java.io.Serializable
@Autowired var withObject: UserBean? = null
@Autowired var withString: String?=null
@Autowired var withInt: Int=-1
@Autowired var withBoolean: Boolean=false
@Autowired var withShort: Short = -1
@Autowired var withLong: Long = -1
@Autowired var withFloat: Float = -1f
@Autowired var withDouble: Double = -1.0
@Autowired var withChar: Char = 'a'
@Autowired var withByte: Byte = -1
@Autowired var withCharSequence: CharSequence?=""
@Autowired var withParcelable: Parcelable? = null
@Autowired var withSerializable: Serializable? = null
@Autowired var withBundle: Bundle? = null
//array
@Autowired var withByteArray: ByteArray? = null
@Autowired var withCharArray: CharArray? = null
@Autowired var withShortArray: ShortArray? = null
@Autowired var withIntArray: IntArray? = null
@Autowired var withLongArray: LongArray? = null
@Autowired var withFloatArray: FloatArray? = null
@Autowired var withDoubleArray: DoubleArray? = null
@Autowired var withBooleanArray: BooleanArray? = null
@Autowired var withStringArray: Array<String>? = null
@Autowired var withCharSequenceArray: Array<CharSequence>? = null
@Autowired var withParcelableArray: Array<Parcelable>? = null
//list
@Autowired var withParcelableArrayList: ArrayList<Parcelable>? = null
@Autowired var withSparseParcelableArray: SparseArray<out Parcelable>? = null
@Autowired var withIntArrayList: ArrayList<Int>? = null
@Autowired var withStringArrayList: ArrayList<String>? = null
@Autowired var withCharSequenceArrayList: ArrayList<CharSequence>? = null
@Autowired var helloService: HelloService? = null//Fragment
val fragment = Router.build("/model/fragment").getInstance() as Fragment
val beginTransaction = supportFragmentManager.beginTransaction()
beginTransaction.add(R.id.fragmentLayout, fragment)
beginTransaction.commit()class MyApp : Application() {
override fun onCreate() {
super.onCreate()
//Route execution log
if (BuildConfig.DEBUG) Router.openLog()
}
}Add to build.gradle file
ksp {
arg("enableRouterLog", "true") //compile time log
}-keep class com.hearthappy.router.generate.interceptor.** { *; }
-keep class com.hearthappy.router.generate.routes.** { *; }
-keep class com.hearthappy.router.generate.path.** { *; }
-keep class com.hearthappy.router.generate.provider.** { *; }
-keep class com.hearthappy.router.enums.**{ *;}
-keep class * implements com.hearthappy.router.service.**{*;}
-keep class * implements com.hearthappy.router.interfaces.**{*;}
//If you need to pass in a custom object, create a class that implements the SerializationService interface and use the @Route annotation. For example:
@Route(RouterPath.SERVICE_JSON)
class JsonService:SerializationService {
private val gson=Gson()
override fun toJson(instance: Any?): String? {
return gson.toJson(instance)
}
override fun <T> fromJson(input: String?, clazz: Type?): T {
return gson.fromJson(input,clazz)
}
}//priority: The smaller the value, the higher the priority.
@Interceptor(priority = 1, name = "RoomInterceptor")
class RoomInterceptor : IInterceptor {
override fun intercept(sorter: Sorter, callback: InterceptorCallback) {
if (sorter.getPath() == RouterPath.CASE_ACTIVITY_FOR_RESULT) {
Log.d("RoomInterceptor", "intercept: Execute Interceptor 2")
/**
* onContinue: Continue to execute the route and do not execute subsequent interceptors
* onInterrupt: Interrupt the route jump, and subsequent interceptors will also be interrupted
* If neither onContinue nor onInterrupt is used, all interceptors will be executed in order of priority level, and then the route will be started
*/
callback.onContinue(sorter)
}
callback.onContinue(sorter)
}
}@Route(RouterPath.SERVICE_PATH_REPLACE)
class PathReplaceImpl : PathReplaceService {
override fun forString(path: String): String {
// Custom logic
return path
}
/**
* test uri:hearthappy://kotlin.ksp.com/model2/ui?name=KSP Router!&age=18
* @param uri Uri
* @return Uri
*/
override fun forUri(uri: Uri): Uri {
if (uri.path == RouterPath.MODULES2_UI) {
return uri.buildUpon().apply {
clearQuery()
appendQueryParameter("name", "KSP Router!")
appendQueryParameter("age", "30")
}.build()
}
return uri
}
}//Open Services
interface HelloService: ProviderService {
fun sayHello(name: String): String
}
@Route(RouterPath.SERVICE_HELLO)
class HelloServiceImpl:HelloService {
override fun sayHello(name: String): String {
return "Hello $name"
}
}//Discover service
//Method 1:
@Autowired var helloService: HelloService? = null
override fun onCreate(savedInstanceState: Bundle?) {
Router.inject(this)
super.onCreate(savedInstanceState)
helloService.sayHello("KSP Router")
}
//Method 2:
val helloService1 = Router.build(RouterPath.SERVICE_HELLO).getInstance() as HelloService
val sayHello1 = helloService1.sayHello("KSP Router")
//Method 3:
val helloService2 = Router.getInstance(HelloService::class.java)
val sayHello2 = helloService2.sayHello("KSP Router")//New version of api
//1. Register
private val activityResultLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> ...}
//2、Jump
activityResultLauncher.launch(Intent(this@ForResultActivity, Router.build(RouterPath.MODULES2_UI).getDestination())) //Legacy API
//1、Register
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {}
//2、Jump
Router.build(RouterPath.MODULES2_UI).navigation(this@ForResultActivity, 100, object : NavigationCallback {
override fun onFound(sorter: Sorter) {
Log.d(TAG, "onFound: ${sorter.getPath()}")
}
override fun onLost(sorter: Sorter) {
Log.d(TAG, "onLost: ${sorter.getPath()}")
}
override fun onArrival(sorter: Sorter) {
Log.d(TAG, "onArrival: ${sorter.getPath()}")
}
override fun onInterrupt(sorter: Sorter) {
Log.d(TAG, "onInterrupt: ${sorter.getPath()}")
}
})Migration steps:
- Add
classpath "io.github.hearthappy:router-migration-plugin:1.0.2"to the project rootbuild.gradle - Or integrate the migration plugin with the
plugins {}style - In the module that needs migration, for example
app/build.gradle, addapply plugin: 'io.hearthappy.router.migration' - In every module that needs migration, you must also add the
routerMigration {}configuration block in that module'sbuild.gradle - After Gradle sync, run the
migrateArouterToRoutertask to complete one-click migration - If you run
.\gradlew migrateArouterToRouterfrom the project root, then every submodule that appliesio.hearthappy.router.migrationmust definerouterMigration {}; if one module is missing it, the build will fail - For more migration details, see Migration.md
Example using buildscript + apply plugin:
buildscript {
repositories {
mavenLocal()
google()
mavenCentral()
}
dependencies {
classpath "io.github.hearthappy:router-migration-plugin:1.0.2"
}
}apply plugin: 'io.hearthappy.router.migration'routerMigration {
scanDir = rootDir
dryRun = false
reportFile = file("$buildDir/reports/router-migration/report.txt")
includeFileSuffixes = [".kt", ".java", ".gradle", ".gradle.kts"] as Set
excludeDirectoryNames = [".git", ".gradle", ".idea", "build", "out"] as Set
enableGradleDependencyMigration = true
routerCoreDependency = "io.github.hearthappy:router-core:1.0.2"
routerCompilerDependency = "io.github.hearthappy:router-compiler:1.0.2"
kspPluginVersion = "1.9.24-1.0.20"
}Example using plugins {}:
plugins {
id 'io.hearthappy.router.migration' version '1.0.2'
}routerMigration {
scanDir = rootDir
dryRun = false
reportFile = file("$buildDir/reports/router-migration/report.txt")
includeFileSuffixes = [".kt", ".java", ".gradle", ".gradle.kts"] as Set
excludeDirectoryNames = [".git", ".gradle", ".idea", "build", "out"] as Set
enableGradleDependencyMigration = true
routerCoreDependency = "io.github.hearthappy:router-core:1.0.2"
routerCompilerDependency = "io.github.hearthappy:router-compiler:1.0.2"
kspPluginVersion = "1.9.24-1.0.20"
}Run the migration task:
./gradlew migrateArouterToRouterNotes:
routerMigration {}should be configured in thebuild.gradleof the module that you want to migrate, for exampleapp/build.gradle- If multiple business modules need migration, every module that applies the migration plugin must declare its own
routerMigration {} - If one module applies the plugin but does not configure
routerMigration {}, running.\gradlew migrateArouterToRouterfrom the project root will fail
Press ctrl+shift+R, search for xxx, replace it with yyy, and then click Replace All
| ARouter code (original xxx) | Router code (After replacement yyy) |
|---|---|
com.alibaba.android.arouter.facade.annotation.Route |
com.hearthappy.router.annotations.Route |
com.alibaba.android.arouter.facade.annotation.Interceptor |
com.hearthappy.router.annotations.Interceptor |
com.alibaba.android.arouter.facade.annotation.Autowired |
com.hearthappy.router.annotations.Autowired |
com.alibaba.android.arouter.facade.template.IProvider |
com.hearthappy.router.service.ProviderService |
com.alibaba.android.arouter.facade.service.PathReplaceService |
com.hearthappy.router.service.PathReplaceService |
com.alibaba.android.arouter.facade.service.SerializationService |
com.hearthappy.router.service.SerializationService |
com.alibaba.android.arouter.facade.Postcard |
com.hearthappy.router.launcher.Sorter |
com.alibaba.android.arouter.facade.callback.InterceptorCallback |
com.hearthappy.router.interfaces.InterceptorCallback |
com.alibaba.android.arouter.facade.template.IInterceptor |
com.hearthappy.router.interfaces.IInterceptor |
| ARouter code (original xxx) | Router code (After replacement yyy) |
|---|---|
Postcard |
Sorter |
| ARouter code (original xxx) | Router code (After replacement yyy) |
|---|---|
ARouter.getInstance() |
Router |
| ARouter code (original xxx) | Router code (After replacement yyy) |
|---|---|
navigation\(\s*([a-zA-Z0-9_.$]+)\s*::\s*class\s*\.\s*java\s*\) |
getInstance($1::class.java) |
navigation\(\s*\)\s*as\s+(\w+) |
getInstance() as $1 |