美文网首页
【转】Android 组件化 — 路由

【转】Android 组件化 — 路由

作者: zelo | 来源:发表于2017-11-16 14:29 被阅读36次

    什么是路由,是为了解决什么问题才产生的;
    业界现状是怎么样的,我们可以做什么来优化当前的问题;
    路由设计思路是怎么样的,该怎么设计比较好;
    如何用注解实现路由表;
    URL的参数如何依赖注入到Activity、Fragement;
    如何HookOnActivityResult,不需要再进行requstCode判断;
    如何异步拦截路由,实现线程切换,不阻塞页面跳转;
    如何用Apt实现Retrofit接口式调用;
    如何找到Activity的调用方;
    如何实现路由的安全调用;
    如何避开Apt不能汇总所有Module路由的问题。

    什么是路由

    根据路由表将页面请求分发到指定页面。

    使用场景

    1. App接收到一个通知,点击通知打开App的某个页面
    2. 浏览器App中点击某个链接打开App的某个页面
    3. 运营活动需求,动态把原生的页面替换成H5页面
    4. 打开页面需要某些条件,先验证完条件,再去打开那个页面
    5. 不合法的打开App的页面被屏蔽掉
    6. H5打开链接在所有平台都一样,方便统一跳转
    7. App存在就打开页面,不存在就去下载页面下载,只有Google的App Link支持

    为什么要有路由

    Android原生已经支持AndroidManifest去管理App跳转,为什么要有路由库,这可能是大部分人接触到Android各种Router库不太明白的地方,这里我讲一下我的理解

    1. 显示Intent:项目庞大以后,类依赖耦合太大,不适合组件化拆分
    2. 隐式Intent:协作困难,调用时候不知道调什么参数
    3. 每个注册了Scheme的Activity都可以直接打开,有安全风险
    4. AndroidMainfest集中式管理比较臃肿
    5. 无法动态修改路由,如果页面出错,无法动态降级
    6. 无法动态拦截跳转,譬如未登录的情况下,打开登录页面,登录成功后接着打开刚才想打开的页面
    7. H5、Android、iOS地址不一样,不利于统一跳转

    怎么样的路由才算好路由

    路由说到底还是为了解决开发者遇到的各种奇葩需求,使用简单、侵入性低、维护方便是首要条件,不影响你原来的代码,写入代码也很少,这里就要说说我的OkDeepLink的五大功能了,五大功能瞬间击中你的各种痛点,早点下班不是梦。

    1. 编译时注解,实现静态路由表,不再需要在臃肿的AndroidManifest中找到那个Actvity写Scheme和Intent Filter
    2. 异步拦截器,实现动态路由,安全拦截、动态降级难不倒你
    3. 模仿Retrofit接口式调用,实现方式用apt,不耗性能,参数调用不再是问题
    4. HookOnActivityResult,支持RxJava响应式调用,不再需要进行requestCode判断
    5. 参数依赖注入,自动保存,不再需要手动写onSaveInstance、onCreate(SaveInstace)、onNewIntent(Intent)、getQueryParamer


      注册路由.jpg
      路由结构图.jpg

    详细比较

    大部分路由库都用Apt(编译时注解)生成路由表,然后用路由表转发到指定页面。

    其实说到底,路由的本质就是注册再转发,围绕着转发可以进行各种操作,拦截,替换,参数获取等等,其他Apt、Rxjava说到底都只是为了方便使用出现的,这里你会发现各种路由库反而为了修复各种工具带来的问题,出现了原来没有的问题,譬如DeepLinkDispatch为了解决Apt没法汇总所有Module路由,每个module都要手动注册,ARouter为了解决Apt没法汇总所有Module路由,通过类操作耗时,才出现分组的概念。

    原理分析

    原理流程图.jpg

    1. 定义路由

    路由定义.jpg

    我这边是完全按照URL规范了,这里要说一下,现在好多方法是把参数定义在path里面的,虽然这样做,有不需要额外传参数的好处,但是这样路由就没有那么灵活,调试起来就没有那么方便了。

    建议有好几款app的公司,host都一样,只有scheme不一样,这样只要替换Scheme就能实现降级,维护也简单。

    2. 路由注册

    AndroidManifest里面的acitivity声明scheme码是不安全的,所有App都可以打开这个页面,这里就产生有两种方式去注册,

    注解产生路由表,通过DispatchActivity转发
    AndroidManifest注册,将其export=fasle,但是再通过DispatchActivity转发Intent,天猫就是这么做的,比上面的方法的好处是路由查找都是系统调用,省掉了维护路由表的过程,但是AndroidManifest配置还是比较不方便的
    

    我现在还是采用了注解,后面我会结合两种方法,将注解自动修改AndroidManifest,对于接入方是没有变动的,方法已经找到了,用自定义Lint扫描出注解相关的Activity,然后用processManifestTask修改Manifest,有个demo了,后面会接入。

    3.生成路由表

    譬如通过Apt把这段代码


    生成


    4.初始化路由表

    这里就要提一下使用Apt会造成每个module都要手动注册

    DeepLinkDispatch是这么做的


    ARouter是通过类查找,就比较耗时了,所以他又加入了分组的概念,按需加载


    ActivityRouter就比较巧妙了,通过Stub项目,其他地方都是provide的,只有主工
    程里面用Apt生成RouterInit类,虽然还是要写module的注解


    天猫 统跳协议 是最简单的,转发一下Intent就可以,但是这样就没法享受注解的好处了。

    5.路由查找

    路由查找就是查找路由表对应的页面,值得提起的就是因为要适应Module接入不同App,Scheme要自动适应,路由表其实是Path---》Activity,这样的话内部跳转的时候ARouterUri是没有的。而我这边是有的,我组装了一个内部的Uri,这样拦截器不会有影响。

    6. 路由分发

    现在所有路由方案分发都是用Activity做分发的,这样做会有这几个缺点

    每次都要启动一个Activity,而Activity就算不写任何代码启动都要0.1秒
    如果是异步等待的话,Activiy要在合适时间finish,不然会有一层透明的页面阻挡操作
    

    对于第一个问题,有两个方法

    QQ音乐是把DispatchActivity设为SingleInstacne,但是这样的话,动画会奇怪,堆栈也会乱掉,后退会有一层透明的页面阻挡操作
    DispatchActivity只在外部打开的时候调用
    

    我选择了第二种

    对于第二个问题,有两个方法

    DispatchActivity再把Intent转发到Service,再finish,这种方法唯一的缺陷是拦截器里面的context是Servcie的activity,就没发再拦截器里面弹出对话框了。
    DispatchActivity在打开和错误的时候finish,如果activity已经finish了,就用application的context去转发路由
    

    我选择了第二种


    其实处理透明Activity阻挡操作可以采用取消所有事件变成无感页面的方法,但是还是觉得会影响activity堆栈没有采用这种方案

    7. 结果返回

    这里我封装了一个库RxActivityResult去捕获onActivityResult,这样能保正流式调用

    譬如拍照可以这样写,先定义一个接口



    然后这样调用


    是不是很简单,原理是这样的,通过封装一个RxResultHoldFragment去处理onActivityResult

    8. 动态拦截

    拦截器是重中之重,有了拦截器可以做好多事情,可以说之所以要做页面路由,就是为了要实现拦截器。ARouter是用线程等待实现的,但是现在有Rxjava了,可以实现更优美的方式。

    先来看一下我做的拦截器的效果.


    是不是很简单,参考了部分OkHttp的实现思路,加入Rxjava,实现异步拦截。

    首先将请求转换成责任链模式RealCallChain,RealCallChain的call方法实际不会执行路由跳转,只有Interceptor里面调用了call.proceed或者call.cancel才会执行.


    接着处理异步的问题,这里用到了Rxjava的AsyncSubject和BehaviorSubject,

    AsyncSubject具有仅释放Observable释放的最后一个数据的特性,作为路由请求的发送器

    BehaviorSubject具有一开始就会释放最近释放的数据的特性,作为路由拦截器的发送器

    具体实现看核心代码

    9. 方法调用

    大部分路由库都是手动拼参数调用路由的,这里模仿了Retrofit接口式调用,受了LiteRouter的启发,不过Retrofit使用了动态代理,我使用的Apt没有性能损耗。

    通过Apt生成每个接口的实际方法

    譬如把SecondService接口


    生成


    然后调用

    SecondService就生成了。

    为了调用方便,直接在Activity或者fragement写这段代码,sampleServive就自动生成了

    但是如果用到MVP模式,不是在Activity里面调用路由,后面会支持在这些类里面自动注入SampleService,现在先用java代码build

    1. 参数获取

    大部分路由库都是手动获取参数的,这样还要传入参数key比较麻烦,这里模仿了ARouter,不过我支持类型更全一些,支持Bundle支持的所有类型,而且不需要在Acitivty的onCreate调用获取代码。

    通过Apt把这段代码



    生成

    1.Module接入不同App

    这里是参考ARouter把path作为key对应activity,这样接入到其他app中,就自动替换了scheme码了


    安全

    现在有好多人用脚本来打开App,然后干坏事,其实时可以用路由来屏蔽掉.

    有三种方法供君选择,不同方法适合不同场景

    1. 签名屏蔽
      就是把所有参数加密成一个数据作为sign参数,然后比对校验,但是这要求加密方法不变,要不然升级了以前的app就打不开了

    2. adb打开屏蔽

    在android5.1手机上,用adb打开的app它的mReferrer为空


    1. 包名过滤



      这三种方法,比较适合的还是签名校验为主,adb过滤为副

    转载地址 :http://www.jianshu.com/p/8a3eeeaf01e8

    企业级独立部署应用:知行办公http://zx.naton.cn
    【总监】十二春秋之,3483099@qq.com
    【Master】zelo,616701261@qq.com
    【运营】运维艄公,897221533@qq.com
    【产品设计】流浪猫,364994559@qq.com
    【体验设计】兜兜,2435632247@qq.com
    【iOS】淘码小工,492395860@qq.com;iMcG33K,imcg33k@gmail.com
    【Android】人猿居士,1059604515@qq.com;思路的顿悟,1217022114@qq.com
    【java】首席工程师MR_W,feixue300@qq.com
    【测试】土镜问道,847071279@qq.com
    【数据】fox009521,42151960@qq.com
    【安全】保密,你懂的。

    相关文章

      网友评论

          本文标题:【转】Android 组件化 — 路由

          本文链接:https://www.haomeiwen.com/subject/eoulvxtx.html