TheRouter路由跳转流程源码分析

689 阅读2分钟

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()

}

至此,路由表的构建及跳转流程分析结束。