{"id":20846922,"url":"https://github.com/dsappteam/anchors","last_synced_at":"2025-04-13T04:59:35.984Z","repository":{"id":37697440,"uuid":"175618279","full_name":"DSAppTeam/Anchors","owner":"DSAppTeam","description":":white_check_mark: Anchors 是一个基于图结构，支持同异步依赖任务初始化 Android 启动框架。其锚点提供 \"勾住\" 依赖的功能，能灵活解决初始化过程中复杂的同步问题。参考 alpha 并改进其部分细节, 更贴合 Android 启动的场景, 同时支持优化依赖初始化流程, 自动选择较优的路径进行初始化。","archived":false,"fork":false,"pushed_at":"2022-11-22T06:37:25.000Z","size":5375,"stargazers_count":825,"open_issues_count":5,"forks_count":79,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-13T04:59:27.044Z","etag":null,"topics":["android","starter","startup"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DSAppTeam.png","metadata":{"files":{"readme":"README-zh.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-14T12:32:39.000Z","updated_at":"2025-03-28T06:40:58.000Z","dependencies_parsed_at":"2022-07-14T03:50:31.953Z","dependency_job_id":null,"html_url":"https://github.com/DSAppTeam/Anchors","commit_stats":null,"previous_names":["yummylau/anchors"],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FAnchors","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FAnchors/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FAnchors/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DSAppTeam%2FAnchors/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DSAppTeam","download_url":"https://codeload.github.com/DSAppTeam/Anchors/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665756,"owners_count":21142123,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","starter","startup"],"created_at":"2024-11-18T02:18:28.041Z","updated_at":"2025-04-13T04:59:35.960Z","avatar_url":"https://github.com/DSAppTeam.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"### Anchors\n\n\u003cimg src=\"https://raw.githubusercontent.com/YummyLau/hexo/master/source/pics/anchors/anchor_logo.png\" width = \"1300\" height = \"150\" alt=\"图片名称\" align=center /\u003e\n\n![](https://travis-ci.org/YummyLau/Anchors.svg?branch=master)\n![Language](https://img.shields.io/badge/language-java-orange.svg)\n![Language](https://img.shields.io/badge/language-kotlin-orange.svg)\n[![](https://jitpack.io/v/DSAppTeam/Anchors.svg)](https://jitpack.io/#DSAppTeam/Anchors)\n\nREADME: [English](https://github.com/YummyLau/Anchors/blob/master/README.md) | [中文](https://github.com/YummyLau/Anchors/blob/master/README-zh.md)\n\n#### 版本更新\n* 1.0.2（2019/06/14） 新增支持直接打开 project 节点\n* 1.0.3（2019/12/11） 新增支持节点等待功能\n* 1.0.4（2019/12/31） 优化线上反馈多线程同步通知下一节点启动的问题\n* 1.0.5（2020/01/20） 新增节点释放监听入口，新增多进程/等待/重启新链等 demo 场景（见 Sample）\n* 1.1.0（2020/05/13） 支持 kotlin 及 DSL 特性\n* 1.1.1 (2020/07/31)  优化 DSL block api\n* 1.1.2 (2020/10/08)  优化依赖树遍历速度，修复 Log-TASK_DETAIL 依赖任务无信息的问题\n* 1.1.3 (2020/11/10)  支持多个 block 节点，AnchorManager不再作为单例开放，支持自定义线程池，taskListener 支持 DSL 选择性覆盖方法\n* 1.1.4（2021/04/28）优化日志并优化多线程方案\n* 1.1.5（2022/06/09）优化多线程方案\n* 1.1.6（2022/06/14）支持动态裁剪后续任务链条\n* 1.1.7（2022/07/19）修复java.lang.UnsupportedOperationException\n* 1.1.8（2022/11/22）修复java.lang.IllegalArgumentException\n\n#### 简介\n\n`Anchors` 是一个基于图结构，支持同异步依赖任务初始化 Android 启动框架。其锚点提供 \"勾住\" 依赖的功能，能灵活解决初始化过程中复杂的同步问题。参考 `alpha` 并改进其部分细节, 更贴合 Android 启动的场景, 同时支持优化依赖初始化流程, 选择较优的路径进行初始化。\n\n关于 `alpha` 的思考，请查看 [关于Android异步启动框架alpha的思考](https://yummylau.com/2019/03/15/%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90_alpha%E7%9A%84%E7%A0%94%E7%A9%B6%E4%B8%8E%E6%94%B9%E8%BF%9B/)\n\n较 `alpha` 的优势\n\n* 支持配置 anchors 等待任务链，常用于 application#onCreate 前保证某些初始化任务完成之后再进入 activity 生命周期回调。\n* 支持主动请求阻塞等待任务，常用于任务链上的某些初始化任务需要用户逻辑确认。\n* 支持同异步任务链\n\n#### 使用需知\n\n\u003e 1. Anchors的设计是为了 app 启动时候做复杂的初始化工作能高效便捷完成，而不是用于业务逻辑中用于初始化某些依赖。\n\u003e 2. api 中设置 anchor 会阻塞等待直到 anchor 完成之后才继续走 AnchorsManager#start 后的代码块，application 中 之所以能这么处理是因为没有频繁的 ui 操作。 anchor 之后的任务会在自主由框架调度，同步任务会通过 handler#post 发送到主线程排队处理，异步任务会通过框架内线程池驱动。\n\u003e 3. 等待功能在不设置 anchor 的场景下使用。如果设置了 anchor ，则等待任务应该是后置于 anchor 避免 uiThead 阻塞。\n\u003e 4. 同异步混合链及 anchor 功能的结合使用，可以灵活处理很多复杂初始化场景，但是要充分理解使用功能时的线程背景。\n\n#### 使用方法\n1. 在项目根目路添加 JitPack 仓库，不再使用 JCenter\n\n\t```\n    allprojects {\n        repositories {\n            ...\n            maven { url 'https://jitpack.io' }\n        }\n    }\n\t```\n\n2. 在 **app** 模块下添加依赖\n\n\t```\n\timplementation 'com.github.DSAppTeam:Anchors:v1.1.8'\n\t```\n\n3. 添加依赖图并启动\n\n    框架支持 java 和 kotlin 语言，针对两种语言，Demo 中构建了 `JDatas` 来实现 java 场景逻辑，`Datas` 类来实现 kotlin 逻辑。以 \"图\" 的形式接收启动依赖集，图的构建节点的链接来实现。\n   \n   \n    ```\n    ==\u003e  以 java 为例子，代码可参考 JDatas 类\n    \n    构建一个 Task, 第一个参数指定 name,也是唯一 id，第二个参数指定该 Task 是否异步运行\n    Task task = new Task(\"name\",false) {\n        @Override\n        protected void run(@NotNull String s) {\n            //todo\n        }\n    };\n    \n    构建一个 Project，Project 是 Task 子类，用于描述多 Task 场景，由于使用 \u003cTaskName\u003e 构建，传递一个工厂统一处理\n    以下构建  task1 \u003c- task2 \u003c- task3 \u003c- task4 逻辑， A -\u003e B 表示 A 依赖 B\n    TestTaskFactory testTaskFactory = new TestTaskFactory();\n    Project.Builder builder = new Project.Builder(\"name\", testTaskFactory);\n    builder9.add(\"task1Name\");\n    builder9.add(\"task2Name\").dependOn(\"task1Name\");\n    builder9.add(\"task3Name\").dependOn(\"task2Name\");\n    builder9.add(\"task4Name\").dependOn(\"task4Name\");\n    Project project = builder.build();\n    \n    组合，其实上述 project#dependOn 就是一种组合方式\n    project.dependOn(task);\n        ...\n    通过各种组合即可构建一张依赖图，之后调用 start 传递图头部节点即可启动\n    AnchorsManager.getInstance()\n        .start(task);\n    \n    需要打开调试时 \n    AnchorsManager.getInstance()\n        .debuggable(true)\n        .start(task);\n    \n    需要设置 anchor 时，\u003canchorYouNeed\u003e 代表某些 task，这些 task 需要在 Application#onCreate 结束前保证初始完毕\n    AnchorsManager.getInstance()\n        .addAnchors(anchorYouNeed)\n        .start(task);\n    \n    对于某些场景，你可能需要监听某个 task 的运行状态，则可使用 block 阻塞功能，阻塞等待后可根据业务逻辑解除/破坏等待，waitTaskYouNeed 为你所需要等待的任务\n    AnchorsManager anchorsManager = AnchorsManager.getInstance();\n    LockableAnchor lockableAnchor = anchorsManager.requestBlockWhenFinish(waitTaskYouNeed); \n    lockableAnchor.setLockListener(...){\n        //lockableAnchor.unlock() 解除等待，继续任务链\n        //lockableAnchor.smash() 破坏等待，终止任务链\n    }\n    anchorsManager.start(task);\n    \n    ==\u003e koltin 也支持上述所有流程，同时也提供了 dsl 的构建形式构建依赖图，代码可参考 Datas 类\n    通过调用 graphics 方法来描述一张依赖图，使用 \u003cTaskName\u003e 构建，传递一个工厂统一处理\n    AnchorsManager.getInstance()\n        .debuggable { true }\n        .taskFactory { TestTaskFactory() }     //根据id生成task的工厂\n        .anchors { arrayOf(TASK_93, TASK_10) } //anchor 对应的 task id\n        .block(\"TASK_10000\") {\t\t\t       // block 场景的 task id 及 处理监听的 lambda\n            //根据业务进行  it.smash() or it.unlock()\n        }\n        .graphics {\t\t\t\t\t\t\t      // 构建依赖图\n            UITHREAD_TASK_A.sons(\n                    TASK_10.sons(\n                            TASK_11.sons(\n                                    TASK_12.sons(\n                                            TASK_13))),\n                    TASK_20.sons(\n                            TASK_21.sons(\n                                    TASK_22.sons(TASK_23))),\n    \n                    UITHREAD_TASK_B.alsoParents(TASK_22),\n    \n                    UITHREAD_TASK_C\n            )\n            arrayOf(UITHREAD_TASK_A)\n        }\n        .startUp()\n    其中 anchorYouNeed 为你所需要添加的锚点, waitTaskYouNeed 为你所需要等待的任务,dependencyGraphHead 为依赖图的头部。\n    ```\n\n#### Sample\n\n代码逻辑请参考 **app** 模块下的 sample。\n\n下面针对 demo 中涉及的主要场景做下阐述。\n\n* 多进程初始化\n\n    **SampleApplication.class** 中针对多进程进行实践，满足绝大部分初始化场景。```SampleApplication#onCreate```\n    会在涉及新进程业务启动时被再次调用，所以不同进程的初始化场景可根据进程名称进行特定定制。\n    代码可参考 ```SampleApplication#initDependenciesCompatMultiProcess```  . \n    触发拉起新进程可参考 ```MainActivity#testPrivateProcess```  或者  ```MainActivity#testPublicProcess``` 。\n\n* 某初始化链中间节点需要等待响应\n\n    某些非常苛刻的初始化链可能需要等待某些条件。（注意：这里的响应应该是 UI 线程的响应，如果是异步响应，则可以作为一个节点提前主动初始化了。）比如某些 app 初始化的时候需要用户选择 ”兴趣场景“ 进而初始化后续页面的所有逻辑等。代码可参考 ```MainActivity#testUserChoose```\n\n* 某初始化链完成之后可能会再启动另一条新链\n\n    这类功能也支持，但是实际上框架更提倡在 application 中统一管理所有初始化链。因为框架强调的是 **任意初始化任务应该是属于业务重量级初始化代码或者第三方SDK初始化** 。\n    代码可参考 ```MainActivity#testRestartNewDependenciesLink``` 。\n\n* 初始化链条中需要根据某个任务运行结果修改后续任务链条\n  \n    支持在定义task时可以通过重写```Task#modifySons```方法获取当前任务的后续任务，可以根据需要进行裁剪后返回新的后续任务id列表。（注意：只能在原有的任务链条中进行删减不能新增）\n    代码可参考 ```MainActivity#testCutoutTask``` 。\n\t\n\n#### Debug 信息\n\n**debuggale** 模式下能打印不同维度的 log 作为调试信息输出，同时针对每个依赖任务做 `Trace` 追踪, 可以通过 *python systrace.py* 来输出 **trace.html** 进行性能分析。\n\n`Anchors` 定义不同的 **TAG** 用于过滤 log, 需要打开 Debug 模式。\n\n* `Anchors`, 最基础的 TAG\n* `TASK_DETAIL`, 过滤依赖任务的详情\n\n\t```\n\t2019-03-18 14:19:45.687 22493-22493/com.effective.android.sample D/TASK_DETAIL: TASK_DETAIL\n\t======================= task (UITHREAD_TASK_A ) =======================\n\t| 依赖任务 :\n\t| 是否是锚点任务 : false\n\t| 线程信息 : main\n\t| 开始时刻 : 1552889985401 ms\n\t| 等待运行耗时 : 85 ms\n\t| 运行任务耗时 : 200 ms\n\t| 结束时刻 : 1552889985686\n\t==============================================\n\t```\n* `ANCHOR_DETAIL`, 过滤输出锚点任务信息\n\n\t```\n\t2019-03-18 14:42:33.354 24719-24719/com.effective.android.sample W/ANCHOR_DETAIL: anchor \"TASK_100\" no found !\n\t2019-03-18 14:42:33.354 24719-24719/com.effective.android.sample W/ANCHOR_DETAIL: anchor \"TASK_E\" no found !\n\t2019-03-18 14:42:33.355 24719-24719/com.effective.android.sample D/ANCHOR_DETAIL: has some anchors！( \"TASK_93\" )\n\t2019-03-18 14:42:34.188 24719-24746/com.effective.android.sample D/ANCHOR_DETAIL: TASK_DETAIL\n    ======================= task (TASK_93 ) =======================\n    | 依赖任务 : TASK_92\n    | 是否是锚点任务 : true\n    | 线程信息 : Anchors Thread #7\n    | 开始时刻 : 1552891353984 ms\n    | 等待运行耗时 : 4 ms\n    | 运行任务耗时 : 200 ms\n    | 结束时刻 : 1552891354188\n    ==============================================\n\t2019-03-18 14:42:34.194 24719-24719/com.effective.android.sample D/ANCHOR_DETAIL: All anchors were released！\n\t```\n\t\n* `LOCK_DETAIL`, 过滤输出等待信息\n\n\t```\n\t2019-12-11 14:53:11.784 6183-6437/com.effective.android.sample D/LOCK_DETAIL: Anchors Thread #9- lock( TASK_10 )\n\t2019-12-11 14:53:13.229 6183-6183/com.effective.android.sample D/LOCK_DETAIL: main- unlock( TASK_10 )\n\t2019-12-11 14:53:13.229 6183-6183/com.effective.android.sample D/LOCK_DETAIL: Continue the task chain...\n\t\n\t```\n\n* `DEPENDENCE_DETAIL`, 过滤依赖图信息\n\n\t```\n\t2019-03-18 14:27:53.724 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_9_start(1552890473721) --\u003e TASK_90 --\u003e TASK_91 --\u003e PROJECT_9_end(1552890473721)\n\t2019-03-18 14:27:53.724 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_9_start(1552890473721) --\u003e TASK_90 --\u003e TASK_92 --\u003e TASK_93 --\u003e PROJECT_9_end(1552890473721)\n\t2019-03-18 14:27:53.724 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_8_start(1552890473721) --\u003e TASK_80 --\u003e TASK_81 --\u003e PROJECT_8_end(1552890473721)\n\t2019-03-18 14:27:53.724 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_8_start(1552890473721) --\u003e TASK_80 --\u003e TASK_82 --\u003e TASK_83 --\u003e PROJECT_8_end(1552890473721)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_7_start(1552890473720) --\u003e TASK_70 --\u003e TASK_71 --\u003e PROJECT_7_end(1552890473720)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_7_start(1552890473720) --\u003e TASK_70 --\u003e TASK_72 --\u003e TASK_73 --\u003e PROJECT_7_end(1552890473720)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_6_start(1552890473720) --\u003e TASK_60 --\u003e TASK_61 --\u003e PROJECT_6_end(1552890473720)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_6_start(1552890473720) --\u003e TASK_60 --\u003e TASK_62 --\u003e TASK_63 --\u003e PROJECT_6_end(1552890473720)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_5_start(1552890473720) --\u003e TASK_50 --\u003e TASK_51 --\u003e PROJECT_5_end(1552890473720)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_5_start(1552890473720) --\u003e TASK_50 --\u003e TASK_52 --\u003e TASK_53 --\u003e PROJECT_5_end(1552890473720)\n\t2019-03-18 14:27:53.725 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_4_start(1552890473720) --\u003e TASK_40 --\u003e TASK_41 --\u003e TASK_42 --\u003e TASK_43 --\u003e PROJECT_4_end(1552890473720)\n\t2019-03-18 14:27:53.726 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_3_start(1552890473720) --\u003e TASK_30 --\u003e TASK_31 --\u003e TASK_32 --\u003e TASK_33 --\u003e PROJECT_3_end(1552890473720)\n\t2019-03-18 14:27:53.726 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_2_start(1552890473719) --\u003e TASK_20 --\u003e TASK_21 --\u003e TASK_22 --\u003e TASK_23 --\u003e PROJECT_2_end(1552890473719)\n\t2019-03-18 14:27:53.726 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e PROJECT_1_start(1552890473719) --\u003e TASK_10 --\u003e TASK_11 --\u003e TASK_12 --\u003e TASK_13 --\u003e PROJECT_1_end(1552890473719)\n\t2019-03-18 14:27:53.726 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e UITHREAD_TASK_B\n\t2019-03-18 14:27:53.726 22843-22843/com.effective.android.sample D/DEPENDENCE_DETAIL: UITHREAD_TASK_A --\u003e UITHREAD_TASK_C\n\t```\n\n#### 效果对比\n\n下面是没有使用锚点和使用锚点场景下, **Trace** 给出的执行时间\n\n\u003cimg src=\"https://raw.githubusercontent.com/YummyLau/hexo/master/source/pics/anchors/anchor_1.png\" width = \"1860\" height = \"400\" alt=\"图片名称\" align=center /\u003e\n\n依赖图中有着一条 `UITHREAD_TASK_A -\u003e TASK_90 -\u003e TASK_92 -\u003e Task_93`依赖。假设我们的这条依赖路径是后续业务的前置条件,则我们需要等待该业务完成之后再进行自身的业务代码。如果不是则我们不关系他们的结束时机。在使用锚点功能时，我们勾住 `TASK_93`，则从始端到该锚点的优先级将被提升。从上图可以看到执行该依赖链的时间缩短了。\n\n\u003e 依赖图用于解决任务执行时任务间的依赖关系，而锚点设置则是用于解决执行依赖与代码调用点之间的同步关系。\n\n\n#### 期望\n编写该项目只是希望能提高日常开发的效率，专注于处理业务 。如果更好的做法或者意见建议，欢迎写信到 yummyl.lau@gmail.com, 提出 **Issues** 或发起 **Pull requests** , 任何问题都会第一时间得到处理解决。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsappteam%2Fanchors","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdsappteam%2Fanchors","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdsappteam%2Fanchors/lists"}