前言:路由可以说是模块化开发必备技能了,它可以很方便实现模块之间页面跳转,传参等等操作,而且管理起来也方便,在我们实际开发中页面直接跳转可能不仅仅 从A跳转到B那么简单,比如我从A页面跳转到B页面需要判断用户是否登录了,如果没有登录就跳转到登录页面.那么此时路由的拦截就发挥作用了,在此之前我们可能都是在每个页面的setContentView之前进行判断,这样代码重复量以及可读性,维护性都不强。今天就简单给大家介绍下Arouter路由(阿里路由)
1.Arouter路由集成(kt篇,如果是java开发请自行参考https://github.com/alibaba/ARouter文档)
(1)在gradle里面配置moudle-name,
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' kapt { arguments { arg("AROUTER_MODULE_NAME", project.getName()) } }
其中AROUTER_MODULE_NAME只是一个key值可以自行命名.
(2)添加Arouter相关依赖
api 'com.alibaba:arouter-api:1.5.0' kapt 'com.alibaba:arouter-compiler:1.2.2'
添加依赖需要注意的是:如果模块之间依赖了,那么主模块添加上面两个,然后子模块添加kapt 'com.alibaba:arouter-compiler:1.2.2'这个就行。但是需要注意的是必须每个模块都保证有这两个依赖。而且都要配置moudlename
到此Arouter一些基础配置就差不多了,后面就直接可以用到项目里面了.总体来说配置都不难,只是需要注意是kt还是java开发,然后保证每个模块都有配置moudle-name以及依赖api 'com.alibaba:arouter-api:1.5.0' ,kapt 'com.alibaba:arouter-compiler:1.2.2'
(3)Arouter基础使用:
我在这里就直接说出我个人使用Arouter跳转及传参方法了,每个人使用方法也不一样,可自行参考,择优!
1.配置路由表:
class ARouterPath { companion object { //登录 const val LOGIN_PATH = "/user/view/activity/LoginActivity" //主页面 const val MAIN_PATH = "/healthcdxt/view/activity/MainActivity"
}
这里配置了两个路由表,一个主页面一个登陆页面,现在我们需要从主页面跳转到登录页面。
2.创建activity添加注解
@Route(path = ARouterPath.MAIN_PATH) class MainActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) initTab() }
}
@Route(path = ARouterPath.LOGIN_PATH) class LoginActivity : BaseActivity(){ override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) }
在使用Arouter跳转的activity都需要添加Route注解,然后利用path来配置当前activity的路由即我们在第一步配置的路径,比如:
ARouterPath.LOGIN_PATH就是登录页面配置的路由,ARouterPath.MAIN_PATH就是主页面配置的路由。在配置路由的时候需要注意路由的路径至少2级目录以上,最好配置成------------------/模块名/包名/类名。Ok,路由表以及路由都配置好了,那么接下来就是利用路由进行跳转了。
3.路由跳转和传值
1.简单的跳转不传值:
ARouter.getInstance().build(ARouterPath.LOGIN_PATH) .navigation()
Arouter类查看源码便知是由单列生成的
public static ARouter getInstance() { if (!hasInit) { throw new InitException("ARouter::Init::Invoke init(context) first!"); } else { if (instance == null) { synchronized (ARouter.class) { if (instance == null) { instance = new ARouter(); } } } return instance; } }
然后build传的就是在路由表配置的路径, const val LOGIN_PATH = "/user/view/activity/LoginActivity"。这个路径表示你要跳转的页面。后面的navigation可以拿到相应的对象。在这里就不过多阐述.此外还可以在跳转时添加跳转动画:
ARouter.getInstance().build(ARouterPath.LOGIN_PATH) .withTransition(R.anim.slide_right_in, R.anim.slide_left_out) .navigation(mActivity)
添加动画直接调用withTransition方法,具体动画实现就看各自需求了。
2.跳转时传值
(1)传值:
ARouter.getInstance().build(ARouterPath.LOGIN_PATH) .withString("topath", "tologin") .navigation()
传值也很方便直接跳转Arouter里面的withxxx方法。比如上面我传一个字符串直接调用.withString("topath", "tologin"),其中第一个参数key,第二个为对应的value。需要注意的是:这里的key必须与接收方保持一致.如果需要传其他类型值,也是同样方式,参考下图传就可以满足你的需求了。
(2)接收值( Arouter传值中接收值才是重点也是特别需要注意的点),我这里是用kt,如果是java请另行参考.
1.在写项目时如果你知道这个页面需要接收值。那么第一步就是在页面onCreate中添加Arouter注入
ARouter.getInstance().inject(this)
这一步是接收值的关键的一步。不然很有可能你接收不到值。
2.定义接收值:回到上面说到的传值withString("topath", "tologin"),其中的topath就是key,那么我们在接收值的时候定义的变量一定要与其保持一致,接收方定义如下:
@Autowired @JvmField var topath: String? = null
当然你也可以
@Autowired(name="topath") @JvmField var topath: String? = null
特别提醒:在kt中接收值是这样,在java中就不一样。这里我就不贴java相关的了,ok,页面跳转以及传值到此就差不多了。下面就说下Arouter拦截器,这也是我们在项目中必不可少的。而且使用也是非常方便,简单.
4.Arouter实现路由拦截
(1)拦截定义:何为拦截?我想从A页面跳转到B页面,此时我要拦截你的跳转不让你从A到B,fuck,那么我就从A到C再到B.差不多这就是拦截的意义.
(2)自定义拦截器:既然我们要实现路由拦截,那么什么情况拦截,拦截后需要搞什么梗?这个都需要我们自己去定义,首先看下自定义拦截器.它的定义标识了你的路由跳转什么时候拦截,什么时候不拦截。
@Interceptor(priority = 8, name = "登录拦截器") class MyInterceptor : IInterceptor, BaseActivity() { var mContext: Context? = null override fun process(postcard: Postcard?, callback: InterceptorCallback?) { //如果有extras值就不拦截 if (ConfigParam.NOINTERCEPTOR == postcard!!.extras!!.getString(ConfigParam.NOINTERCEPTOR)) { callback!!.onContinue(postcard) } else { //这是我项目中用到的路由,需要换成你自己的 when (postcard!!.path) { ARouterPath.STARTUP_PATH, ARouterPath.LOGIN_PATH, ARouterPath.REGISTER_PATH, ARouterPath.MAIN_PATH, ARouterPath.RESETTING_PASSWORD, ARouterPath.GESTUREPASSWORD_LOGIN, ARouterPath.CODE_LOGIN, ARouterPath.UNREGISTERED_PATH, ARouterPath.SUPPORT_PATH, ARouterPath.SYSTEM_INTRODUCE, ARouterPath.DOWNAPPURL -> { //不拦截 callback!!.onContinue(postcard) } else -> { //如果需要登录 if (!Helper.getSharedPreferences(mContext!!).getBoolean( ConfigParam.IS_LOGIN, true ) || Helper.getSharedPreferences(mContext!!).getBoolean( ConfigParam.OVER_TIME, true ) ) //拦截 { callback!!.onInterrupt(null) } else { //不拦截 callback!!.onContinue(postcard) } } } } } override fun init(context: Context?) { mContext = context } }
OMG,这么多判断,什么拦截,什么不拦截,仿佛一看当场晕倒...骚年先来一根烟压压惊稳住..........
其实里面的逻辑并不难,我先把拦截流程讲完,再给大家说下这里拦截的逻辑.
定义好了拦截器,就是怎么在路由跳转进行拦截了。这就简单了。
ARouter.getInstance().build(ARouterPath.ORDER_MANAGER) .withTransition(R.anim.slide_right_in, R.anim.slide_left_out) .navigation(mActivity, MyNavigationCallback())
假如我要从主页面跳转到路由表对应的ARouterPath.ORDER_MANAGER页面,但是我需要拦截此次路由跳转,直接在navigation里面加上一个NavigationCallback接口实现类.我们看下MyNavigationCallback源码
inner class MyNavigationCallback : NavigationCallback { override fun onLost(postcard: Postcard?) { } override fun onFound(postcard: Postcard?) { } override fun onInterrupt(postcard: Postcard?) { Log.e("onInterrupt", "onInterrupt") Log.e("currentThread", Thread.currentThread().name) mActivity.runOnUiThread { Log.e("currentThread22", Thread.currentThread().name) ARouter.getInstance().build(ARouterPath.LOGIN_PATH) .withString("topath", postcard!!.path) .navigation() } } override fun onArrival(postcard: Postcard?) { Log.e("onArrival", "onArrival") } }
这个类实现了四个方法,onFound表示拦截之前调用,onInterrupt拦截时调用,onArrival跳转到目的页面后调用。一般我们只用到这个三个方法。上面说到要从主页面跳转到ARouterPath.ORDER_MANAGER页面,但是此时我需要判断是否登录,如果没有登录需要拦截,拦截onInterrupt跳转到登录页面,可以看到跳转到登录页面传了一个path值,这个值就是ARouterPath.ORDER_MANAGER路由,当跳转到登录页面用户登录后,就直接跳转到ARouterPath.ORDER_MANAGER页面了,一套简单的路由拦截就差不多了。
梳理一下:定义拦截器,然后路由跳转时实现NavigationCallback接口,在拦截方法中添加自己的拦截目的。
接下来说下自定义拦截器和NavigationCallback逻辑:
先给大家模拟两个场景:1.我需要A页面跳转到B但是需要判断是否登录,如果没有登录就跳转到C。(这个场景就是文章中的)
2.结合场景一,假如我在A页面中点击某个菜单进入页面B,此时需要判断是否登录,没有就先跳转到登录页面C。但是此时我又是新用户需要注册,需要从C进入注册页面D,在注册页面D中添加用户信息我需要进入页面B。前面我们对B路由进行拦截了,此时我们从D跳转到B是不是又重新回到登录页面C了?自始至终都无法从D进入B。
场景模拟完了,看代码逻辑怎么处理这两种场景
@Interceptor(priority = 8, name = "登录拦截器")
class MyInterceptor : IInterceptor{
}
首先自定义拦截器要实现IInterceptor接口,并添加Interceptor注解.注解中priority线程优先级别,nam就是一个标识。如果你有多个拦截器两个值不能一致.唯一注意的就是priority值越小优先级越高.
IInterceptor接口有两个方法。一个init一个process.init在编译的时候就执行了,也就是MyInterceptor 类加载的时候。在as编译的时候检测到MyInterceptor 会自动生成一个拦截器相关的类。然后就是process方法。这个方法就是我们自定义拦截器逻辑实现重点了。不过需要注意的是这个方法是在子线程执行的。我们先看下面的逻辑(代码细节问题请忽略)
//如果有extras值就不拦截 if (ConfigParam.NOINTERCEPTOR == postcard!!.extras!!.getString(ConfigParam.NOINTERCEPTOR)) { callback!!.onContinue(postcard) } else { when (postcard!!.path) { ARouterPath.STARTUP_PATH, ARouterPath.LOGIN_PATH, ARouterPath.REGISTER_PATH, ARouterPath.MAIN_PATH, ARouterPath.RESETTING_PASSWORD, ARouterPath.GESTUREPASSWORD_LOGIN, ARouterPath.CODE_LOGIN, ARouterPath.UNREGISTERED_PATH, ARouterPath.SUPPORT_PATH, ARouterPath.SYSTEM_INTRODUCE, ARouterPath.DOWNAPPURL -> { //不拦截 callback!!.onContinue(postcard) } else -> { //如果需要登录 if (!Helper.getSharedPreferences(mContext!!).getBoolean( ConfigParam.IS_LOGIN, true ) || Helper.getSharedPreferences(mContext!!).getBoolean( ConfigParam.OVER_TIME, true ) ) //拦截 { callback!!.onInterrupt(null) } else { callback!!.onContinue(postcard) } } } }
先看这部分代码。先用postcard.path拿到想跳转的页面路由。我们项目中并不是所有页面都需要判断登录,因此在我们添加不需要判断登录的页面路由,调用 callback!!.onContinue(postcard)。onContinue顾名思义就是继续你的操作,我不拦截你了。那么不是上面的路由跳转就需要判断是否登录了,怎么判断是否登录这个需要根据自己项目需求来判定。如果需要登录就callback!!.onInterrupt(postcard),onInterrupt顾名思义就是拦截,如果不需要登录就callback!!.onContinue(postcard)不拦截。如果拦截了,怎么做?我们再来看下MyNavigationCallback源码
inner class MyNavigationCallback : NavigationCallback { override fun onLost(postcard: Postcard?) { } override fun onFound(postcard: Postcard?) { } override fun onInterrupt(postcard: Postcard?) { Log.e("onInterrupt", "onInterrupt") Log.e("currentThread", Thread.currentThread().name) mActivity.runOnUiThread { Log.e("currentThread22", Thread.currentThread().name) ARouter.getInstance().build(ARouterPath.LOGIN_PATH) .withString("topath", postcard!!.path) .navigation() } } override fun onArrival(postcard: Postcard?) { Log.e("onArrival", "onArrival") } }
需要注意onInterrupt也是在子线程中执行的。onInterrupt里面跳转我就不再说了上面已经说得很详细,再说下onFound,onArrival。onFound方法是在拦截之前执行的,而onArrival是到达目的页面后执行.在这两个方法中根据实际情况做操作.
我们回到MyInterceptor 中。还剩下extras不拦截。这个又是什么梗?又回到我们模拟的场景2.假如我们在D跳转到B页面传递一个值,这个值标识不管登录与否都不需要拦截。
然后在跳转时传一个
ConfigParam.NOINTERCEPTOR, ConfigParam.NOINTERCEPTOR
当跳转的时候拦截器检查到这个值根据自定义拦截器,我们对此次路由跳转不拦截,那么我们就完美解决场景2的问题了。
此次路由跳转,传值,拦截就告一段落了,文章可能重复的内容比较多,但是根据思路来应该差不多能收货不少。
最后祝各位.工作愉快,健健康康。顺便贴上群里290611780,欢迎各位勇士加入!!!