Arouter路由之拦截器(重点)

时间:2024-03-26 16:12:43

前言:路由可以说是模块化开发必备技能了,它可以很方便实现模块之间页面跳转,传参等等操作,而且管理起来也方便,在我们实际开发中页面直接跳转可能不仅仅 从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必须与接收方保持一致.如果需要传其他类型值,也是同样方式,参考下图传就可以满足你的需求了。

Arouter路由之拦截器(重点)

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

        }

    }
}

先看Arouter路由之拦截器(重点)这部分代码。先用postcard.path拿到想跳转的页面路由。我们项目中并不是所有页面都需要判断登录,因此在Arouter路由之拦截器(重点)我们添加不需要判断登录的页面路由,调用  callback!!.onContinue(postcard)。onContinue顾名思义就是继续你的操作,我不拦截你了。那么不是上面的路由跳转就需要判断是否登录了,Arouter路由之拦截器(重点)怎么判断是否登录这个需要根据自己项目需求来判定。如果需要登录就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页面传递一个值,这个值标识不管登录与否都不需要拦截。Arouter路由之拦截器(重点)

 

然后在跳转时传一个

ConfigParam.NOINTERCEPTOR, ConfigParam.NOINTERCEPTOR

Arouter路由之拦截器(重点)

当跳转的时候拦截器检查到这个值根据自定义拦截器,我们对此次路由跳转不拦截,那么我们就完美解决场景2的问题了。

此次路由跳转,传值,拦截就告一段落了,文章可能重复的内容比较多,但是根据思路来应该差不多能收货不少。

最后祝各位.工作愉快,健健康康。顺便贴上群里290611780,欢迎各位勇士加入!!!

 

 

 

 

 

 

 

 

 

 

       

  

 

 

  1.