はい、TheRouter は私が作成しました

補足:
オープンソースウェアハウスアドレス:github.com/HuolalaTech…

そうです、Lala のオープン ソース ルーティング ライブラリ - TheRouter は私が作成したものです

2017年末から2018年初頭にかけて、モジュラー開発の経験とピットに足を踏み入れるプロセスについてよく話していました。たとえば、「Android モジュラー プラットフォームの設計」、「モジュール間の結合をエレガントに削除する」、「エンタープライズ Android モジュラー プラットフォームの設計に関する提案」などの記事が当時書かれていました。

しかし、モジュール化では、より一般的な解決策を要約することはできますが、他の人にそれを明確に説明するのは難しいことがわかったので、これらについて話すのをゆっくりとやめました. 誤解されやすいのは、私たちは小さな会社であり、モジュール化を行う必要がないということです。また、会社の既存のインフラストラクチャとシステムのいくつかの制限に基づいていたため、比較的完全なモジュラー ソリューションを外部にオープン ソース化することはできませんでしたが、完全なモジュラー ソリューションをオープンソース化するための種が植えられました。

TheRouterに戻る

この名前は、実際、私をよく知っている人には知られています. 私は以前にそれと呼ばれるオープンソースのMVPフレームワークを書きました.TheMVPそれは基本的にActivityP層アーキテクチャと見なされる業界標準になっています. その後、Alipay で使用され、Settings-About-Copyright 情報で見つけることができます.数日前に逆コンパイルするまで、私はまだBaseActivity私のコードが使用されていることを確認しました.

は一意性を表し、これで十分であることを示します。

TheRouterTheRouter使ってみれば、今のエンタープライズレベルのAndroidモジュール化のあり方がよくわかると思います。

TheRouter を使用する理由

ルーティングは、今日の Android 開発、特にエンタープライズ レベルのアプリに不可欠な機能でありIntent、ページ ジャンプの強い依存関係を切り離し、クロスチーム開発の相互依存の問題を軽減するために使用できます。

大規模な APP 開発では、基本的にモジュラー (またはコンポーネント化) 方式での開発が行われ、モジュール間のより高いデカップリングが必要になります。TheRouterこれは、モジュール開発のための完全なソリューション セットであり、従来のモジュール依存関係の分離とページ ジャンプをサポートするだけでなく、モジュール化プロセスで一般的な問題に対するソリューションも提供します。たとえば、モジュール開発後のコンポーネントではApplicationライフます。

しかし、なぜそれを使用するのか、最終的な分析では、それでもARouter使用するには頭痛の種が多すぎます。

  • 1 つは厳格で、すべてのルートがハードコーディングされていますが、柔軟にCrashオンライン ページを一時的に H5 にダウングレードしたい場合は、多くのコードを変更する必要があり、多くの制限があります。
  • もう 1 つは効率性ですが、コンパイル時間であろうと起動時間であろうと、この 2 つの問題は解決されていません。とある工場のオープンソースプロジェクトはこんな感じで、作者は昇進して異動し、あとはそのまま放置ということで、いじくり回しはKPIになじまないので、仕事の報告は仕方がない。まさか、時間のかかる指標を開始させてくれた自分でやってください。
  • パッチを使用すると、同じブランチのパッケージが製品パッケージ コードtinkerとは異なることがわかり、パッチのサイズが直接増加します。ARouterButterknife
  • もちろん、多くの違いがあります。この表を見てください。
関数 TheRouter ARouter WMRouter
フラグメント ルーティング ✔️ ✔️ ✔️
依存性注入のサポート ✔️ ✔️ ✔️
ルーティング テーブルの読み込み ランタイム スキャン
なし 反射なし
実行時にdex
リフレクション インスタンス クラスをスキャンすると
パフォーマンスが大幅に低下する
実行時のファイルの読み取りは、インスタンス クラスのパフォーマンスの低下を
反映しています。
注釈付き正規表現 ✔️ ✖️ ✔️
活動指定インターセプター ✔️(四大拦截器可根据业务定制) ✖️ ✔️
导出路由文档 ✔️(路由文档支持添加注释描述) ✔️ ✖️
动态注册路由信息 ✔️ ✔️ ✖️
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-ActionManager

当用户执行某些操作(打开某个页面、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,可能有点慢,耐心等待

TheRouter 移行ツール

おすすめ

転載: juejin.im/post/7140153936295493668