ARouter路由框架使用,登录拦截实践中的小问题
阅读原文时间:2021年04月22日阅读:1

ARouter路由框架实现登录拦截,以及注意点

当前项目在做模块化、组件化的开发实践,这其中就少不了路由框架的使用。项目选用阿里的ARouter路由框架,最近在实现登录拦截功能时候,遇到一点小问题,特此记录。

一、登录拦截Inteceptor实现,官方文档

按照官方文档以及Demo的代码示例,设置场景为某个Activity需要登录方能调用,就需要在该Activity上添加路由以及配置响应标记extra其中为int

//路由注解,以及extra标记为该页面需要登录
@Route(path = RouterPath.ROUTER_PATH_WEB_ACTIVITY, extras = 10086)
class WebActivity : BaseWebActivity() {

    companion object {
        const val INTENT_EXTRA_URL = "url"
    }

    //注解,ARouter url传递来的参数名,可以通过@Autowired(name=“urlParam”)字段改写本地的名称,java中需要public,koltin中需要lateinit,
    @Autowired(name = "url")
    lateinit var urlStr: String//用于显示web的url

    //覆写基类中的成员变量,并赋值
    override val urlParam: String
        get() = urlStr

    override fun onCreate(savedInstanceState: Bundle?) {
        ARouter.getInstance().inject(this)
        super.onCreate(savedInstanceState)

    }
}

路由框架中inteceptor都是AOP切面编程的,在ARouter中一般的路由跳转都会经过拦截器的。除非在ARouter.getInstantce()进行跳转的时候标记了greenChannel

  • 登录拦截器LoginInteceptor

    @Interceptor(priority = 9, name = "LoginInteceptor")
    class LoginInterceptor : IInterceptor {
    @SuppressLint("CheckResult")//Single的rx结果,不处理了,
    override fun process(postcard: Postcard?, callback: InterceptorCallback?) {
        //拦截操作,去调用登录
        if (postcard != null && postcard.extra == ARouterConstants.AROUTER_POSTCARD_EXTRA_NEED_LOGIN) {
            val isLogin = SPUtils.getInstance().getBoolean("isLogined")
            if (isLogin) {
                callback!!.onContinue(postcard)
            } else {
                //跳转登录
    
                //拦截器运行在ARouter的子线程池中,若要启动activity,必须切换到主线程操作。而且,ARouter所有一般的跳转都会走拦截器,
                //如此,就会形成死循环,所以跳转登录,就需要greenChannel,忽略拦截
                Single.just(0)
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(Consumer<Int> {
                        ARouter.getInstance()
                            .build(RouterPath.ROUTER_PATH_LOGIN_ACTIVITY)
                            //拦截跳转web H5的时候,后续登录成功后,继续跳转,需要这个url
                            .withString(
                                WebActivity.INTENT_EXTRA_URL,
                                postcard.extras.getString(WebActivity.INTENT_EXTRA_URL)
                            )
                            .greenChannel()
                            .withFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                            .navigation()
                    })
                callback!!.onInterrupt(null)//路由中断
            }
        }
    }
    
    override fun init(context: Context?) {}
    }

登录拦截器的实现,如上。其中需要注意点:

  1. 一般登录拦截会要跳转登录页,在process中,就不要再用ARouter的方式调用登录,因为每次路由都会走拦截器,这就形成了死循环。如果要用,就要添加.greenChannel()来规避拦截。
  2. Inteceptor都是运行在ARouter内部的一个线程池中,在这里不要切换到主线程才能生效。(个人测试如此,或许有其他解决方式)
  3. 要根据逻辑必须配置callbackonInterrupt或者onContinue
  4. 判断跳转目的地,是否需要登录拦截,根据的是目的地页面配置的extraint标记值,所以这里用postcard.extra取值
二、问题所在

如上可以简单使用,是官方文档,也是Demo中的示例方式。然而多数情况下的业务需求,并不是这样。比如WebActivity有些传来的URL界面是需要登录,有些则不必登录,而在WebActivity上配置了extra就意味着每种URL都要拦截。

1、解决方案可以是配置CommonWebActivity继承WebActivity,配置不同的路由,并且不添加extra标记。这就需要,在URL传递方,来根据是否登录,调用不同的路由。

2、或者那些不需要登录拦截的URL调用时候,都配置greenChannel来规避拦截,但是如果有多组拦截器策略,就会被同时忽略掉了。

如上的解决方案,都少都有些不符合生产业务场景。我们想要的是可以在代码中动态配置哪些跳转需要登录拦截,或者其他拦截标记,哪些不需要。extra的标记,其实在ARouter.getInstance().build("route—path")之后,会生成postcard的对象,这个就是本次路由跳转信息的一个载体。postcard.setExtra(Int)有这个api,所以就想:

val postcard = ARouter.getInstance()
            .build(RouterPath.ROUTER_PATH_WEB_ACTIVITY)
        //设置本次跳转的extra标记,想要每次不同的调用,配置不同的extra标记,
        postcard.extra=10010
        //继续原有跳转
        postcard
            .withString(
                WebActivity.INTENT_EXTRA_URL,
                "http://www.google.com"
            ).navigation(activity)

似乎如上,就是我们想要的效果。然而、然而、然而、:事与愿违,如此并不能配置extra=10010,也就不能在Inteceptor中根据postcard.extra来判断了。

因为两个postcard经过路由后,不是同一个对象了,最重要的是,其他值都还好,就是extra这个标记值,代码修改的是不会传递给后面生成的postcard的,而会被@Route(path="web",extra=10086)中的extra覆盖掉。这个可以看ARouter源码,一步步debug下去,就会知道在warehouse的一个map赋值RouteMeta时候,覆盖的。有在github上提交issues,似乎看到有官方回复,暂时不会处理。所以有遇到这个问题的朋友,留心一下。

**另:**在ARouter的跳转startActivityForResultFragment中调用,则在fragmentonActivityResult不会接收到回调。会被上层Activity拦截掉。解决方案:

  1. 使用原生的context.startActivityForResult调用(不是activity调用,否则也是一样,不能下发),需要注意如果Activity覆写onActivityResult内部需要调用super才能下发。

  2. 如果用ARouter调用的话,可以在Activity中重写onActivityResult并手动下发

     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            val fragments = supportFragmentManager.fragments
            for (fragment in fragments) {
                fragment.onActivityResult(requestCode,resultCode,data)
            }
        }
  3. 类似方法1,只是在模块化开发时,跨模块调用,出现不能引用到目的地Activity的时候,来个半原生的方式跳转

    //ARouter原生不支持fragment中的forResult,官方也不予以处理,这里变相的取到跳转目的地 的 class对象
            val postcard = ARouter.getInstance().build(RouterPath.ROUTER_PATH_LOGIN_ACTIVITY)
            LogisticsCenter.completion(postcard)//这不操作必不可少,完成postcard的参数赋值
            startActivityForResult(Intent(context, postcard.destination), FRAGMENT_REQUEST_CODE_LOGIN)

后记: ARouter还在逐步摸索使用中,慢慢学习,遇到后续问题,再做记录。