补充:
开源库房地址: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
下发补丁的时分,发现同一个分支打出来的包,ARouter
和Butterknife
的产品包代码都不相同,直接增大了补丁体积。 - 当然,还有许多差异,看这个表格吧。
功能 | TheRouter | ARouter | WMRouter |
---|---|---|---|
Fragment路由 | ✔️ | ✔️ | ✔️ |
支撑依靠注入 | ✔️ | ✔️ | ✔️ |
加载路由表 | 无运行时扫描 无反射 |
运行时扫描dex(新版别改为反射) 反射实例类 性能损耗大 |
运行时读文件 反射实例类 性能损耗中 |
注解正则表达式 | ✔️ | ✖️ | ✔️ |
Activity指定拦截器 | ✔️(四大拦截器可根据事务定制) | ✖️ | ✔️ |
导出路由文档 | ✔️(路由文档支撑增加注释描绘) | ✔️ | ✖️ |
动态注册路由信息 | ✔️ | ✔️ | ✖️ |
APT支撑增量编译 | ✔️ | ✔️(敞开文档生成则无法增量编译) | ✖️ |
plugin支撑增量编译 | ✔️ | ✖️ | ✖️ |
多 Path 对应同一页面(低成本完成双端path统一) | ✔️ | ✖️ | ✖️ |
远端路由表下发 | ✔️ | ✖️ | ✖️ |
支撑单模块独立初始化 | ✔️ | ✖️ | ✖️ |
支撑运用路由翻开第三方库页面 | ✔️ | ✖️ | ✖️ |
支撑运用路由翻开第三方库页面 | ✔️ | ✖️ | ✖️ |
对热修正支撑(例如tinker) | ✔️(未改动的代码屡次构建无变动) | ✖️(屡次构建apt产品会产生变化,生成无意义补丁) | ✖️(屡次构建apt产品会产生变化,生成无意义补丁) |
动态页面路由才干
其实单纯的页面路由,没什么好说的,基本上一切人都是这么做的。APT编译期生成一个描绘类,gradle
插件聚合一切的描绘类,运用发动的时分再加载描绘类,就这么一个流程。TheRouter
文档里面写的十分详细了,这儿首要讲讲路由在现代APP中要怎样用。
TheRouter
从规划阶段,考虑的便是APP动态化才干。所以既能支撑第三方SDK
的路由跳转,也能支撑插件化的开发形态,又能处理H5Hybrid
、Flutter
混合的这种项目,横竖路由表都是能够随意增加。
那,真实用处最多的是经过动态下发,提高客户端容灾才干。
比方在线上,某些页面或许中心下单买卖流程由于客户端开发忽略,形成无法运用的情况,能够经过路由将对应页面降级为H5或许小程序,保证线上APP
仍然是可用的状况。
有两种推荐的长途下发办法可供运用方选择:
- 将打包体系与装备体系打通,每次新版别APP打包后主动将
assets/
目录中的装备文件上传到装备体系,下发给对应版别APP 。优点在于全主动不会出错。 - 装备体系无法打通,线上手动下发需求修改的路由项,由于
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
都是能够被盯梢的,只需你愿意,能够在日志中将一切的动作调用栈输出,以便利调试运用。
当用户履行某些操作(翻开某个页面、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…