Skip to content
xiaojinzi123 edited this page Jan 13, 2020 · 5 revisions

为啥访问不到真正的 Intent?

因为 URL 获取 Intent 和发起一个跳转的本质差不多, 只不过前者是拿到 Intent 不使用罢了. 但是假如你得拦截器有作一些耗时得操作、或者阻塞得去跳转界面拿数据. 那你此操作就和触发一次真实的跳转的没啥区别了?这是绝对不行的. 所以不会让用户能拿到 真正目标界面的 Intent.

而有些框架很有趣, 能跳转所有拦截器直接拿到 URL 对应的 Activity 的 Intent, 比如 ARouter 就可以, 有些人就觉得人家为啥可以, 你怎么不可以?这问题很有趣, 你觉得人家提供了 Api 解决了你的问题, 但是这真的正确吗?你要知道拦截器本质上是干嘛的?拦截器所有的请求,做一些全局或者部分 URL 匹配才会做的事情. 拦截器针对的目标是全部的 URL. 并且拦截器拥有改变请求的的能力. 你怎么知道经过若干拦截器修改后的请求就是你想象的目标 Activity. 比如 URL 为 "Router://user/test?type=1", 有一个拦截器是根据请求的参数跳转到不同的界面。这时候发起正常跳转是可能对应两个 Activity, 那么问题来了 "Router://user/test" 你觉得它对应哪个 Activity? 所以总结如下:

  • URL 是一定要经过拦截器的, 否则拦截器的作用将大打折扣, 也可平常理解的拦截器概念有违背
    • 用户可能在拦截器中执行耗时操作
    • 用户可能在拦截器中跳转到一个其他界面去拿到数据, 再接下去流程(比如登录拦截器, 就是帮助没有登录的实现登录, 再继续流程)
  • URL 的真实跳转的目标可能是不确定的

基于上述总结的两点, 不到发起 Intent 跳转这句代码之前, 你是不可能拿到真正的 Intent

你想不发起跳转拿到 Intent, 下面两点是相互矛盾的.

  • 不经过拦截器你根本不知道你的请求是否会被更改.
  • 但是经过拦截器又可能跳出中间界面真正的执行了一些代码. 比如日志、记录、数据库操作、登录拦截器 等等

设计 ProxyIntent 的目的

有些时候, 正如一位使用者提的 issue 所说的一样, 总有一些时候你是需要拿到 Intent 交给系统的, 比如小部件、通知等等.

跳转的这部分不能像自己平常跳转一样使用 Router.with(context).host("xxx").path("xxx").forward();

所以设计 ProxyIntent 的目的就是为了解决这个问题. 您可以拿到一个 Intent, 并且这个 Intent 交给系统跳转后还能最终无感的跳转到真正的目标界面

什么是 ProxyIntent

我们知道, 一般情况下, 我们使用组件化框架的路由功能都是达到了一个目的:

  • 真正的 Intent 被一个 URL 所代替, 你可以理解为你访问不到真正的 Intent, 只能通过 URL 去访问. URL 可以看做是真正的 Intent 的一个别名. 系统会解析 URL 到真正的 Intent 的.

在我们 Android 中, 其实一个 Activity 的跳转其实就是一个 Intent, 只不过这个 Intent 的意图就是指向了 Activity, 整理上述所说的. 我们可以认为. URL--> Intent --> Activity

我们全程都应该直接和 URL 打交道, 而不应该绕过 URL 去拿到 Intent 或者目标的 Activity.

这时候 ProxyIntent 的作用就体现出来了. 首先. ProxyIntent 本质上是一个 Intent, 你可以通过以下方式构建出一个 ProxyIntent

Intent intent = Router.newProxyIntentBuilder()
                .host("user")
                .path("personCenter")
  		// 可省略, 省略后默认使用框架自带的无界面的 Activity
                .proxyActivity(this.getClass())
                .buildProxyIntent();

构建出来的 Intent 就是一个 ProxyIntent, 它有两部分组成:

  • 这个 ProxyIntent 的意图的目标是一个代理的 Activity. 如上面代码所示, 这个代理 Activity 可以自定义.
  • 这个 ProxyIntent 中有 跳转到真正的目标界面所需的所有的参数和数据.

整个跳转流程

当你拿到一个 ProxyIntent, 假设你创建了一个通知栏消息, 点击消息的触发的 Intent 就是刚刚你拿到的 ProxyIntent. 那么流程如下:

  • 点击消息 --> 跳转到代理 Activity

  • 代理 Activity 解析参数拿到真正的目标界面的 URL 和所需的其他数据(Bundle、flags、categories....)

  • 代理 Activity 根据上一步拿到的 URL 和所需数据发起跳转.

  • 真正的目标界面被正确的打开

其中的代理 Activity 可以是框架默认的也可以是自定义的. 如果是自定义的. 还请在 onCreate 方法和 onNewIntent() 方法中添加如下的代码:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ......
        startProxyRouter(getIntent().getExtras());
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        startProxyRouter(intent.getExtras());
    }

    private void startProxyRouter(@Nullable Bundle bundle) {
        if (Router.haveProxyIntent(bundle)) {
            Router.with(this)
                    .proxyBundle(bundle)
                    .forward();
        }
    }

并且你得关注你自定义的代理 Activity 的启动模式. 比如消息的触发, 代理 Activity 存在或者不存在的时候都可能触发. 我这边建议使用 singleInstance.

代码

具体的代码可以查看 Component 项目的源码中的示例模块 app 中的 MainAct 界面. 有两个按钮可以分别生成两个通知. 一个使用默认的代理 Activity, 一个使用 MainAct 为代理 Activity. 无论程序是否运行, 点击通知两者都可完成跳转.

Clone this wiki locally