补充:
开源库房地址:github.com/HuolalaTech…

没错,货拉拉开源的路由库 —— TheRouter 是我写的

大约在17年底到18年头的时分,我经常会讲一些其时做模块化开发的心得和踩坑进程。比方这几篇都是那时分写的:《Android 模块化渠道规划》、《高雅移除模块间耦合》、《企业级 Android 模块化渠道规划主张》。

但后来我渐渐不讲这些了,由于我发现做模块化,尽管咱们能总结出来一套较为通用的处理计划,但很难经过几次短短的技能分享就跟他人讲清楚。而且很简单让人产生误解:咱们是小公司,不需求做模块化。再加上由于其时是基于公司已有的基础建设,和制度的一些约束,并不能对外开源一套较为完善的模块化计划,开源一套完好的模块化计划这个种子就一直埋下了。

说回 TheRouter

这个姓名,其实了解我的都知道,之前写过一个开源类 MVP 框架,叫TheMVP,基本上成为了一种将Activity看做 P 层架构的职业规范。后来被支付宝运用了,也在 设置-关于-版权信息 里面能查到,直到前几天我去反编译的时分,都还看到BaseActivity用的是我的代码。

The 代表了一种仅有性,表明有这个就够了。

TheRouter也是相同,我信任用过TheRouter以后你才会真实意识到,现在的企业级Android模块化应该怎样玩。

为什么要运用 TheRouter

路由是现如今 Android 开发中必不可少的功能,尤其是企业级APP,能够用于将Intent页面跳转的强依靠关系解耦,一起减少跨团队开发的相互依靠问题。

关于大型 APP 开发,基本都会选用模块化(或组件化)办法开发,关于模块间解耦要求更高。 TheRouter 是一整套彻底面向模块化开发的处理计划,不仅能支撑惯例的模块依靠解耦、页面跳转,一起供给了模块化过程中常见问题的处理办法。例如:完美处理了模块化开发后组件内无法获取 Application 生命周期与事务流程,形成每次初始化与相关依靠调用都需求跨模块修改代码的问题。

不过为什么要用,说到底,仍是用ARouter用的太头疼了。

  • 一个是呆板,一切路由都是写死的,凡是想灵敏一点,把线上Crash的页面降级成H5暂时处理,都得改一大堆代码还许多约束性。
  • 另一个便是功率,不管是编译时长仍是发动耗时,这俩问题都一直不处理。某个厂的开源项目都这样,作者们该提高的提高,该转岗的转岗,剩下的躺平不管,毕竟修修补补这事不占KPI,没法述职啊。没办法,自己来吧,谁让咱们还有发动耗时指标的。
  • 再便是遇到的一个坑,在用tinker下发补丁的时分,发现同一个分支打出来的包,ARouterButterknife的产品包代码都不相同,直接增大了补丁体积。
  • 当然,还有许多差异,看这个表格吧。
功能 TheRouter ARouter WMRouter
Fragment路由 ✔️ ✔️ ✔️
支撑依靠注入 ✔️ ✔️ ✔️
加载路由表 无运行时扫描
无反射
运行时扫描dex(新版别改为反射)
反射实例类
性能损耗大
运行时读文件
反射实例类
性能损耗中
注解正则表达式 ✔️ ✖️ ✔️
Activity指定拦截器 ✔️(四大拦截器可根据事务定制) ✖️ ✔️
导出路由文档 ✔️(路由文档支撑增加注释描绘) ✔️ ✖️
动态注册路由信息 ✔️ ✔️ ✖️
APT支撑增量编译 ✔️ ✔️(敞开文档生成则无法增量编译) ✖️
plugin支撑增量编译 ✔️ ✖️ ✖️
多 Path 对应同一页面(低成本完成双端path统一) ✔️ ✖️ ✖️
远端路由表下发 ✔️ ✖️ ✖️
支撑单模块独立初始化 ✔️ ✖️ ✖️
支撑运用路由翻开第三方库页面 ✔️ ✖️ ✖️
支撑运用路由翻开第三方库页面 ✔️ ✖️ ✖️
对热修正支撑(例如tinker) ✔️(未改动的代码屡次构建无变动) ✖️(屡次构建apt产品会产生变化,生成无意义补丁) ✖️(屡次构建apt产品会产生变化,生成无意义补丁)

动态页面路由才干

其实单纯的页面路由,没什么好说的,基本上一切人都是这么做的。APT编译期生成一个描绘类,gradle插件聚合一切的描绘类,运用发动的时分再加载描绘类,就这么一个流程。TheRouter 文档里面写的十分详细了,这儿首要讲讲路由在现代APP中要怎样用。

TheRouter 从规划阶段,考虑的便是APP动态化才干。所以既能支撑第三方SDK的路由跳转,也能支撑插件化的开发形态,又能处理H5HybridFlutter混合的这种项目,横竖路由表都是能够随意增加。

那,真实用处最多的是经过动态下发,提高客户端容灾才干。
比方在线上,某些页面或许中心下单买卖流程由于客户端开发忽略,形成无法运用的情况,能够经过路由将对应页面降级为H5或许小程序,保证线上APP仍然是可用的状况。

有两种推荐的长途下发办法可供运用方选择:

  1. 将打包体系与装备体系打通,每次新版别APP打包后主动将assets/目录中的装备文件上传到装备体系,下发给对应版别APP 。优点在于全主动不会出错。
  2. 装备体系无法打通,线上手动下发需求修改的路由项,由于 TheRouter 会主动用最新下发的路由项覆盖包内的路由项。优点在于精确,且流量资源占用小。

注:一旦你设置了自定义的InitTask,原框架内路由表初始化使命将不再履行,你需求自己处理找不到路由表时的兜底逻辑,一种主张的处理办法见如下代码。

// 此代码 有必要 在 Application.super.onCreate() 之前调用
RouteMap.setInitTask(new RouterMapInitTask() {
    /** 
     * 此办法履行在异步
     */
    @Override
    public void asyncInitRouteMap() {
        // 此处为纯事务逻辑,每家公司远端装备计划或许都不相同
        // 不主张每次都恳求网络,不然恳求网络的过程中,路由表是空的,或许形成APP无法跳转页面
        // 最好是优先加载本地,然后开异步线程加载远端装备
        String json = Connfig.doHttp("routeMap");
        // 主张加一个判断,假如远端装备拉取失利,运用包内装备做兜底计划,不然或许形成路由表反常
        if (!TextUtils.isEmpty(json)) {
            List<RouteItem> list = new Gson().fromJson(json, new TypeToken<List<RouteItem>>() {
            }.getType());
            // 主张远端下发路由表差异部分,用远端包覆盖本地更合理
            RouteMap.addRouteMap(list);
        } else {
            // 在异步履行TheRouter内部兜底路由表
            initRouteMap()
        }
    }
});

另一种情况,假如某些页面传参过程中,漏传了一些固定参数,也能够经过动态下发路由表的办法,对不同的页面,做动态的默认参数注入,这样就能到达不发版也能直接修正某些参数引起的小问题。

TheRouter中下发路由表的格式:

[  {    "path": "https://kymjs.com/therouter/test",    "className": "com.therouter.app.autoinit.TestActivity",    "action": "",    "description": "",    "params": {    	"key":"value"    }  },  ......]

单模块主动初始化才干

其实,做模块化最费事的两个点,第一个是依靠解耦,第二个应该便是独立模块的初始化问题了。再加上现在关于隐私合规问题越查越严,各种权限都有必要在隐私弹窗授权以后才干运用,使得模块独立更难,动不动就得改到Application壳工程。

TheRouter 的单模块主动初始化才干便是为了处理这样的情况,能够只在当时模块声明初始化办法后,将会在事务场景时主动被调用。

类似于 Gradle 的 Task,你也能够声明自己的初始化 Task,然后声明的时分供给好需求依靠的其他 Task,这样只需依靠的那个 Task 没有初始化,你的使命就不会被初始化。直到依靠的那个 Task 初始化完成,你的使命才会被主动调用。

/**
 * 将会在异步履行
 */
@FlowTask(taskName = "mmkv_init", dependsOn = TheRouterFlowTask.APP_ONCREATE, async = true)
public static void test2(Context context) {
    System.out.println("异步=========Application onCreate后履行");
}

@FlowTask 注解参数阐明:

  • taskName:当时初始化使命的使命名,有必要全局仅有,主张格式为:moduleName_taskName

  • dependsOn:参阅Gradle Task,使命与使命之间或许会有依靠关系。假如当时使命需求依靠其他使命先初始化,则在这儿声明依靠的使命名。能够一起依靠多个使命,用英文逗号分隔,空格可选,会被过滤:dependsOn = “mmkv, config, login”,默认为空,运用发动就被调用

  • async:是否要在异步履行此使命,默认false。

内置初始化节点

运用这个才干,在路由内部默认支撑了两个生命周期类使命,可在运用时直接引证

  • TheRouterFlowTask.APP_ONCREATE:当Application的onCreate()履行后初始化
  • TheRouterFlowTask.APP_ONSPLASH:当运用的首个Activity.onCreate()履行后初始化

一起,运用TheRouter的主动初始化依靠,也无需担心循环依靠形成的问题,框架会在编译期构建有向无环图,监测循环依靠情况,假如发现会在编译期直接报错,而且还会将产生循环引证的使命显示出来,用于排错。

动态化才干

还有一个,动态化才干。这个才干其实是需求整个项目公司的配合,比方有一套类似才智大脑的计划,能够基于客户端过去的一些埋点数据,智能推断出用户下一步要做的事情,然后经过长连接直接向客户端下发指令做某些事情。

不过抛开后端的才干,单独靠客户端也是能够运用的。

Action 本质是一个全局的体系回调,首要用于预埋的一系列操作,例如:弹窗、上传日志、清理缓存。
与 Android 体系自带的播送通知类似,你能够在任何地方声明动作与处理办法。而且一切Action都是能够被盯梢的,只需你愿意,能够在日志中将一切的动作调用栈输出,以便利调试运用。

没错,TheRouter 是我写的

当用户履行某些操作(翻开某个页面、H5点击某个按钮、动态页面装备的点击事情)时,将会主动触发,履行预埋的 Action 逻辑。

但仍是强烈推荐,将端上数据与服务端链路打通,根据客户端不同的用户行为,交由后端分析,进而推测出用户下一步动作,提前履行下发逻辑交给客户端履行,则是一套完好的动态化计划。

模块化支撑,Gradle脚本一键切换源码引证

在模块化开发过程中,假如没有选用分仓,或选用了分仓但仍然运用 git-submodule 的办法开发,应该都会遇到一个问题。假如集成包选用源码编译,构建时刻真实太久,大大降低开发调试功率;假如选用aar依靠编译,关于底层模块修改了代码,每次都要重新构建aar,在上层模块修改版别号以后,才干继续整包构建编译,也极大影响开发功率。
TheRouter 中供给了一个 Gradle 脚本,只需求在开发本地的local.properties文件中声明要参加编译的module,其他未声明的默认运用aar编译,这样就能灵敏切换源码与aar,而且不会影响其他人,如下节选代码可供参阅运用:

/**
 * 假如工程中有源码,则依靠源码,不然依靠aar
 */
def moduleApi(String compileStr, Closure configureClosure) {
    String[] temp = compileStr.split(":")
    String group = temp[0]
    String artifactid = temp[1]
    String version = temp[2]
    Set<String> includeModule = new HashSet<>()
    rootProject.getAllprojects().each {
        if (it != rootProject) includeModule.add(it.name)
    }
    if (includeModule.contains(artifactid)) {
        println(project.name + "源码依靠:===project(\":$artifactid\")")
        projects.project.dependencies.add("api", project(':' + artifactid), configureClosure)
//        projects.project.configurations { compile.exclude group: group, module: artifactid }
    } else {
        println(project.name + "依靠:=======$group:$artifactid:$version")
        projects.project.dependencies.add("api", "$group:$artifactid:$version", configureClosure)
    }
}

在实际运用时,能够彻底运用moduleApi 替换掉原有的api。当然, implementation也能够有一个对应的moduleImplementation,这样只需求注释或解注释setting.gradle文件内的include语句就能够到达切换源码、aar的意图了。
具体的运用办法,能够看我这篇文章里面讲的【源码与aar互斥】的完成:xiaozhuanlan.com/topic/27358…

什么年代了,还在用 ARouter?

支撑从 ARouter 一键搬迁!
没错,什么年代了,还在用ARouter?
关于这种已有的存量路由框架,当然也是供给了一键搬迁的图形化东西。
为了写这个东西我也是废了好大的劲,特意学了一遍JavaFX怎样用,然后打了一个Mac产品、一个Windows产品。
不禁感叹:Java的跨渠道才是真实的跨渠道啊。

注:传到了GitHub,或许有点慢,耐性等候

  • Mac OS 搬迁东西下载:github.com/HuolalaTech…
  • Windows 搬迁东西下载:github.com/HuolalaTech…

没错,TheRouter 是我写的