路由可用于处理页面跳转
Activity
和 Fragment
path
与页面多对一关系或一对一关系Path
支持正则表达式声明json
格式路由表导出json
路由表object
跨模块传递(无需序列化,且能保证对象类型)Activity(Fragment)
如果一个页面(支持 Activity、Fragment)允许被路由打开,则需要使用注解 @Route 声明路由项,每个页面允许声明多个路由项,也就是一对多的能力,极大降低多端路由统一时的业务影响面。
参数释义
ActionManager
:https://therouter.cn/docs/2022/08/25/01http://therouter.com/home?hello=world&a=b
@Route(path = "http://therouter.com/home", action = "action://scheme.com",
description = "第二个页面", params = {"hello", "world", "a", "b"})
public class HomeActivity extends AppCompatActivity {
}
如果两条路由的path、目标className完全相同,则认为是同一条路由,不会考虑参数是否相同。
RouteMap.json
文件是TheRouter
路由表的核心。他有两个作用:
RouteMap.json
文件中加入第三方的路由页面。详见【1.4 为第三方库添加路由表】如果以上两个能力你都不需要,那么你可以直接忽略这个 json 文件的存在,即便删除也没事,因为每次编译这个文件都会重新生成。而如果你没有删除,也没有关系,在编译期框架会自动合并相同项目,合并规则遵循 【1.2.1 路由表覆盖规则】
如果你用到了上面的能力,建议你将编译期配置项打开,并设置到error
级别,详见【1.5 编译期配置】
编译期按照如下顺序取并集。
覆盖规则:根据如下顺序,如果相同,后者可以覆盖前者的路由表规则。
业务模块 aar
中的路由表主app module
代码中的路由表assets/RouteMap.json
文件中声明的路由表。
TheRouter项目每次编译后,会在apk内生成一份路由表,默认路径为:/assets/therouter/routeMap.json
同时这份路由表也支持远端动态下发,例如远端可以针对不同的APP版本,下发不同的路由表达到配置目的。因此有两种推荐的方式可供使用方选择:
注:一旦你设置了自定义的InitTask,原框架内路由表初始化任务将不再执行,你需要自己处理找不到路由表时的兜底逻辑,一种建议的处理方式见如下代码。
// 此代码 必须 在 Application.super.onCreate() 之前调用
RouteMapKt.setInitTask(new RouterMapInitTask() {
/**
* 此方法执行在异步
*/
@Override
public void asyncInitRouteMap() {
// 此处为纯业务逻辑,每家公司远端配置方案可能都不一样
// 不建议每次都请求网络,否则请求网络的过程中,路由表是空的,可能造成APP无法跳转页面
// 最好是优先加载本地,然后开异步线程加载远端配置
String json = Connfig.doHttp("routeMap");
// 建议加一个判断,如果远端配置拉取失败,使用包内配置做兜底方案,否则可能造成路由表异常
if (!TextUtils.isEmpty(json)) {
List<RouteItem> list = new Gson().fromJson(json, new TypeToken<List<RouteItem>>() {
}.getType());
// 建议远端下发路由表差异部分,用远端包覆盖本地更合理
RouteMap.addRouteMap(list);
} else {
// 在异步执行TheRouter内部兜底路由表
initRouteMap()
}
}
});
共有两种方式
/**
* @params path 路由Path
* @params className 第三方页面的Activity或Fragment完整类名
* @params action 要向页面传入的动作,如果没有传空字符串
* @params description 第三方页面的文档描述
*/
addRouteItem(RouteItem(path, className, action, description))
从 1.2.3-rc1
版本开始,编译器配置已经迁移到build.gradle
中,具体请查看:1.2.3版本编译改动
路由表中如果声明了一个不存在的Activity/Fragment
,通常是因为旧代码删除,或重构改了包名类名时,忘记清理路由表了,这里可选报错或警告,提供参考。
可在local.properties
配置编译期属性(warning输出日志,error直接报错)
Activity
检查 CHECK_ROUTE_MAP=warning
(warning/error/delete)可选gradle.properties
了assets/therouter/routeMap.json
,需要配置 CHECK_ROUTE_MAP=delete
传入的参数可以是 String
和8种基本数据类型、也可以是Bundle
、Serializable
、
Parcelable
对象,跟 Intent
传值规则一致。
同时也支持为本次跳转的 Intent
添加Flag/Uri/ClipData/identifier
等业务特殊参数。
// 传入参数可以通过注解 @Autowired 解析成任意类型,如果是对象建议传json
// context 参数如果不传或传 null,会自动使用 application 替换
TheRouter.build("http://therouter.com/home")
.withInt("key1", 12345678)
.withString("key2", "参数")
.withBoolean("key3", false)
.withSerializable("key4", object)
.withObject("object", any) // 与上一个方法不同,这个方法可以传递任意对象,但是接收的地方对象类型请保证一致
.navigation(context);
// 如果传入 requestCode,默认使用startActivityForResult启动Activity
.navigation(context, 123);
// 如果要打开的是fragment,需要使用
.createFragment();
// 回调的方式打开页面
.navigation(v.getContext(), new NavigationCallback() {
@Override
public void onFound(@NotNull Navigator navigator) {
super.onFound(navigator);
Toast.makeText(v.getContext(), "找到页面,即将打开" + navigator.getUrl(), Toast.LENGTH_SHORT).show();
}
@Override
public void onLost(@NotNull Navigator navigator) {
super.onLost(navigator);
Toast.makeText(v.getContext(), "丢失页面" + navigator.getUrl(), Toast.LENGTH_SHORT).show();
}
@Override
public void onArrival(@NotNull Navigator navigator) {
super.onArrival(navigator);
Toast.makeText(v.getContext(), "已打开" + navigator.getUrl(), Toast.LENGTH_SHORT).show();
}
});
延迟跳转主要应用场景有两种:
// 暂存的动作可以有多个,会在恢复时按顺序执行
TheRouter.build("http://therouter.com/home")
.withInt("key1", 12345678)
.pending()// 暂存当前跳转动作
.navigation(context);
// 恢复
sendPendingNavigator(); //toplevel方法,无需类名调用,Java请通过NavigatorKt类名调用
使用注解接收对象时,必须调用TheRouter.inject(this);建议直接在 Base 中统一调用。
接收有两种形式:
@Route(path = "http://therouter.com/home")
public class HomeActivity extends BaseActivity {
// 允许解析成8种基本数据类型或对应封装类
@Autowired
int key1;
// String类型可以自动解析,除了String和8种基本类型封装类以外,
// 其他的Object需要自定义解析规则,自定义方式见下文高级功能介绍 2.4 节内容
@Autowired
String key2;
// 如果没有声明 name,则使用变量名作为 Intent 解析的key
@Autowired(name = "key3")
String user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 使用注解接收对象时,必须调用这一句,建议直接在 Base 中统一调用。
TheRouter.inject(this);
// 当然如果不嫌麻烦,也是允许自己手动解析的,但是只能解析成String,因为传的时候都是以String类型传递
String key1 = activity.getIntent().getStringExtra("key1");
}
}
页面跳转间如果要对象,有两种方式:
自定义 @Autowired 解析
TheRouter.inject(this);
建议直接在 Base
中统一调用。Parcelable
或Serializable
对象。(在之前的版本,不支持内部类类型对象注入,1.1.3-rc5
已修复该问题)TheRouter.build("http://therouter.com/home")
.withString("user_info_object", json) // 传递json对象
.withObject("user_object", any) // 这个方法可以传递任意对象,但是接收的地方对象类型请保证一致
.navigation(context);
@Route(path = "http://therouter.com/home")
public class HomeActivity extends BaseActivity {
@Autowired
User user_info_object;
@Autowired
User user_object;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 使用注解接收对象时,必须调用这一句,建议直接在 Base 中统一调用。
TheRouter.inject(this);
}
}
页面跳转间如果要对象,需要先将对象序列化为json,将json传递以后再反序列化为对象
通过 @Autowired
注解可以自定义将 json 解析为对象注入
TheRouter.addAutowiredParser(new MyAutowiredParser());
具体Parser类
定义可参考代码DefaultUrlParser
类
框架内置四种自定义处理器可供业务场景定制,用于在路由跳转过程中,以切面的方式统一修改路由落地页参数信息。
应用场景:用于修复客户端上路由 path 错误问题。
例如:相对路径转绝对路径,或由于服务端下发的链接无法固定https或http,但客户端代码写死了 https 的 path,就可以用这种方式统一。
注:必须在 TheRouter.build() 方法调用前添加处理器,否则处理器前的所有path不会被修改。
NavigatorKt.addNavigatorPathFixHandle(new NavigatorPathFixHandle() {
@Override
public String fix(String path) {
path = path.replace("http://", "https://");
if (!path.contains("www.kymjs.com") && path.startsWith("/")) {
path = "https://www.kymjs.com" + path;
}
return path;
}
/**
* 数字越大,优先级越高,
* 优先级方法通常情况下不需要重写,除非你真的有什么特殊场景会用到
*/
@Override
public int getPriority() {
return 5;
}
});
应用场景:需要将某些path
指定为新链接的时候使用。 也可以用在修复链接的场景,但是与 path 修改器
不同的是,修改器通常是为了解决通用性的问题,替换器只在页面跳转时才会生效,更多是用来解决特性问题。
例如模块化的时候,首页壳模板组件中开发了一个SplashActivity
广告组件作为应用的MainActivity
,在闪屏广告结束的时候自动跳转业务首页页面。
但是每个业务不同,首页页面的 Path
也不相同,而不希望让每个业务线自己去改这个首页壳模板组件,此时就可以组件中先写占位符https://kymjs.com/splash/to/home
,让接入方通过 Path
替换器解决。
注:必须在 TheRouter.build().navigation() 方法调用前添加处理器,否则处理器前的所有跳转不会被替换。
NavigatorKt.addPathReplaceInterceptor(new PathReplaceInterceptor() {
@Override
public String replace(String path) {
if ("https://kymjs.com/splash/to/home".equals(path)) {
return "https://kymjs.com/business/home";
}
return path;
}
});
应用场景:常用在未登录不能使用的页面上。例如访问用户钱包页面,在钱包页声明的时候,可以在路由表上声明本页面是需要登录的,在路由跳转过程中,如果落地页是需要登录的,则先替换路由到登录页,同时将原落地页信息作为参数传给登录页,登录流程处理完成后可以继续执行之前的路由操作。
路由替换器的拦截点更靠后,主要用于框架已经从路由表中根据 path 找到路由以后,对找到的路由做操作。
这种逻辑在所有页面跳转前写不太合适,以前的做法通常是在落地页写逻辑判断用户是否具有权限,但其实在路由层完成更合适。
注:必须在 TheRouter.build().navigation() 方法调用前添加处理器,否则处理器前的所有跳转不会被替换。
// 对于如何判断当前页面是否需要登录,我们推荐在 assets/RouteMap.json 路由表或者注解中配置(会自动加入路由表),例如:
// 用注解定义 needLogin
@Route(path = "http://kymjs.com/home", params = {"needLogin","true"})
public class TestActivity extends AppCompatActivity {}
// 用 assets/RouteMap.json 路由表定义的方式如下
{
"path": "http://kymjs.com/home",
"className": "com.therouter.demo.shell.TestActivity",
"action": "",
"description": "",
"params": {
"needLogin":true
}
}
// 在拦截器处理跳转逻辑
NavigatorKt.addRouterReplaceInterceptor(new RouterReplaceInterceptor() {
@Override
public RouteItem replace(RouteItem routeItem) {
if (Boolean.parseBoolean(routeItem.getExtras().get("needLogin").toString())) {
RouteItem target = matchRouteMap("登录页面的url");
target.setDescription("可以在这修改路由参数,推荐把routeItem作为参数传给登录页");
return target;
}
return routeItem;
}
});
与前三个处理器不同的点在于,路由的AOP拦截器全局只能有一个。用于实现AOP的能力,在整个TheRouter跳转的过程中,跳转前,目标页是否找到的回调,跳转时,跳转后,都可以做一些自定义的逻辑处理。
使用场景:场景很多,最常用的是可以拦截一些跳转,例如debug页面在生产环境不打开,或定制startActivity
跳转方法。
NavigatorKt.setRouterInterceptor(new RouterInterceptor() {
@Override
public void process(RouteItem routeItem, InterceptorCallback callback) {
if (!TheRouter.isDebug() && routeItem.getClassName().equals("com.kymjs.DebugActivity")) {
return;
} else {
// callback 对象也是可以自定义的,看你怎么用了
callback.onContinue(routeItem);
}
}
});
如果使用TheRouter
跳转,传入了一个不识别的的path
,则不会有任何处理。你也可以定义一个默认的全局回调,来处理跳转情况,如果落地页是 Fragment
则不会回调。
当然,跳转结果的回调不止这一个用途,可以根据业务有自己的处理。
回调也可以单独为某一次跳转设置,navigation()
方法有重载可以传入设置。
NavigatorKt.defaultNavigationCallback(new NavigationCallback() {
// 落地页Activity打开后,执行到onCreate会回调
@Override
public void onActivityCreated(@NonNull Navigator navigator, @NonNull Activity activity) {
super.onActivityCreated(navigator, activity);
}
// startActivity执行后会立刻回调
@Override
public void onArrival(@NonNull Navigator navigator) {
super.onArrival(navigator);
}
// 找到待跳转的落地页时就会回调(startActivity之前)
@Override
public void onFound(@NonNull Navigator navigator) {
super.onFound(navigator);
}
// 找不到落地页的时候会回调
@Override
public void onLost(@NonNull Navigator navigator) {
super.onLost(navigator);
}
});
如果返回为空,表示当前url不是路由表内的path
// kotlin toplevel方法,Java调用请使用RouteMapKt类
matchRouteMap("url填这里") == null