Github: github.com/HuolalaTech…
官网: therouter.cn/
核心功能
根据指定URL跳转至对应的页面
示例
@Route(path = "app://main/home", description = "首页")
class HomeActivity : AppCompatActivity(){
@JvmField
@Autowired(name = "module")
var module = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
TheRouter.inject(this)
}
}
TheRouter.build("app://main/home").withString("module", "xxx").navigation();
通过路由path:app://main/home 跳转至指定页面:HomeActivity
带着问题看源码
路由path是如何与页面对应上的?
源码分析
跳转流程
在TheRouter.build()方法中会构建一个Navigator对象:
@JvmStatic
fun build(url: String?): Navigator {
return Navigator(url)
}
在Navigator中解析URL,并把参数放入 extras中:
init {
require(!TextUtils.isEmpty(url), "Navigator", "Navigator constructor parameter url is empty")
uri = Uri.parse(url ?: "")
uri.encodedQuery?.split("&")?.forEach {
val idx = it.indexOf("=")
val key = if (idx > 0) it.substring(0, idx) else it
val value: String? = if (idx > 0 && it.length > idx + 1) it.substring(idx + 1) else null
// 通过url取到的value,都认为是string,autowired解析的时候会做兼容
extras.putString(key, value)
}
}
使用withXxx()方法传入的参数也一样会放入extras中:
fun withString(key: String?, value: String?): Navigator {
extras.putString(key, value)
return this
}
跳转代码分析,代码有删减,只保留关键流程
@JvmOverloads
fun navigation(ctx: Context?, fragment: Fragment?, requestCode: Int, ncb: NavigationCallback? = null) {
// 延迟跳转,解决Activity 不能在后台启动问题。
// 加入 disposableQueue 后,由使用方检测到APP回到前台后,
// 调用 sendPendingNavigator() 方法,重新触发页面跳转
if (!initedRouteMap || pending) {
disposableQueue.addLast(PendingNavigator(this) {
pending = false
this.navigation(ctx, fragment, requestCode, ncb)
})
return
}
val context = ctx ?: getApplicationContext()
val callback = ncb ?: defaultCallback
var matchUrl: String? = simpleUrl
// 改造URL,可实现URL重定向功能
for (interceptor in pathReplaceInterceptors) {
interceptor?.let {
matchUrl = interceptor.replace(matchUrl)
}
}
// 查询路由表
var match = matchRouteMap(matchUrl)
// 处理Action隐式跳转
if (ActionManager.isAction(this) && match == null) {
ActionManager.handleAction(this, context)
return
}
// 目标落地页处理,可实现对传递参数的统一处理,如:encode、decode
match?.getExtras()?.putAll(extras)
for (interceptor in routerReplaceInterceptors) {
interceptor?.let {
match = interceptor.replace(match)
}
}
// 执行跳转
if (match != null) {
callback.onFound(this)
routerInterceptor.invoke(match!!) { routeItem ->
val intent = intent ?: Intent()
...
intent.component = ComponentName(context!!.packageName, routeItem.className)
if (fragment != null) {
fragment.startActivityForResult(intent, requestCode)
} else if (context is Activity) {
context.startActivityForResult(intent, requestCode)
} else {
if (TheRouter.isDebug) {
throw RuntimeException("TheRouter::Navigator context is not Activity or Fragment")
} else {
context.startActivity(intent)
}
}
}
callback.onArrival(this)
} else {
callback.onLost(this)
}
}
简单来讲,主要流程就是:查找路由表,由路由Path找到对应的落地页className,然后通过Context#`` startActivity() 跳转至对应页面。
参数注入
有被@Autowired 注解修饰的属性的类,在编译后会生成一个XXX__TheRouter__Autowired样式的辅助类,在调用TheRouter.inject(this)方法时,会把跳转时传入的参数赋值过去。
构建路由表
在编译期,由APT工具生成路由表工具类:RouterMap__TheRouter__xxxxx
@androidx.annotation.Keep
public class RouterMap__TheRouter__1980638879 implements IRouterMapAPT {
public static void addRoute() {
RouteItem item1 = new RouteItem("app://main/home","com.demo.HomeActivity","","首页");
RouteMapKt.addRouteItem(item1);
}
}
然后用ASM把addRoute()方法调用插入TheRouterServiceProvideInjecter#initDefaultRouteMap()中:
package a;
public final class TheRouterServiceProvideInjecter {
...
public static final void initDefaultRouteMap() {
try {
RouterMap__TheRouter__1980638879.addRoute();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在初始化时加载路由表:
fun init(context: Context?) {
...
initDefaultRouteMap()
}
至此,路由表的构建及跳转流程分析结束。