{"id":16558329,"url":"https://github.com/leifzhang/androidstartup","last_synced_at":"2025-03-16T20:30:29.084Z","repository":{"id":44190362,"uuid":"431806948","full_name":"Leifzhang/AndroidStartup","owner":"Leifzhang","description":"模块化启动框架","archived":false,"fork":false,"pushed_at":"2022-02-11T03:59:29.000Z","size":383,"stargazers_count":138,"open_issues_count":1,"forks_count":13,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-02-27T12:45:48.143Z","etag":null,"topics":["inject","ksp","startup"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","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/Leifzhang.png","metadata":{"files":{"readme":"README.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":"2021-11-25T10:40:41.000Z","updated_at":"2024-07-26T15:14:28.000Z","dependencies_parsed_at":"2022-08-12T11:01:21.710Z","dependency_job_id":null,"html_url":"https://github.com/Leifzhang/AndroidStartup","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Leifzhang%2FAndroidStartup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Leifzhang%2FAndroidStartup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Leifzhang%2FAndroidStartup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Leifzhang%2FAndroidStartup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Leifzhang","download_url":"https://codeload.github.com/Leifzhang/AndroidStartup/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243826798,"owners_count":20354221,"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":["inject","ksp","startup"],"created_at":"2024-10-11T20:10:22.693Z","updated_at":"2025-03-16T20:30:28.782Z","avatar_url":"https://github.com/Leifzhang.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AndroidStartUp\n\n玩具工程\n\n想优化下项目内的启动框架，还是基于有向无环图将任务排序。之后拆分多线程等待执行任务，保证所有初始化任务能够有序的执行，不产生由于别的sdk没有初始化导致的错乱问题。\n\n因为原来的项目是多进程，所以考虑给多进程解耦，开发了一套ksp的注解生成分组逻辑。不同进程可以生成不同的任务分组逻辑。\n\n增加锚点逻辑，让任务链可以更便捷的添加。\n\n支持多线程真等待执行，解决了任务只是按照优先顺序放入，并不会等待前置任务完成，多线程情况下可能出现异常的情况\n\n# 依赖tag化\n\n剔除了传统的类形式的依赖方式，转化成tag，标识当前任务的依赖。\n\n好处就是多模块解耦，劣势就是更加抽象，并不能形成立体的依赖关系。\n\n# task分组\n\n只是增加了个简单的组的概念而已，内部的依赖顺序还是要重新调整。\n\n该功能主要就是服务大型工程，多进程初始化，并且初始化任务复杂，但是同时不同进程间需要初始化的任务也不同。\n\n## 使用\n\n通过ksp，以及添加注解的方式完成组任务生成。\n\n1. build.gradle 添加\n\n```gradle\nplugins {\n    id(\"com.google.devtools.ksp\") version \"1.5.30-1.0.0\"\n}\nkotlin {\n    sourceSets.main {\n        kotlin.srcDir(\"build/generated/ksp\")\n    }\n    sourceSets.test {\n        kotlin.srcDir(\"build/generated/ksp/test/kotlin\")\n    }\n}\ndependencies {\n    implementation project(':startup')\n    implementation project(':startup-annotation')\n    ksp project(\":startup-ksp-compiler\")\n}\n\n```\n\n2. 创建一个任务并添加注解\n\n新增新的启动注解，通过注解的排列组合来生成新的启动任务。\n\n```kotlin\n@Async\n@Await\n@DependOn(\n    dependOn = [AsyncTask1Provider::class, SimpleTask2Provider::class],\n    dependOnTag = [\"taskB\"]\n)\n@Startup(strategy = Process.MAIN)\n@MustAfter\nclass SampleGenerate1Task : TaskRunner {\n\n    override fun run(context: Context) {\n        info(\"MyAnchorTask\")\n    }\n\n}\n```\n\n老的启动任务声明\n\n```kotlin\n@StartupGroup(group = \"ksp\")\n// 可选锚点\n@MustAfter\nclass AsyncTask1 : SimpleStartupTask() {\n\n    override fun mainThread(): Boolean {\n        return false\n    }\n\n    override fun await(): Boolean {\n        return true\n    }\n\n    override fun run(context: Context) {\n        info(\"AsyncTask1\")\n    }\n}\n```\n\n考虑到多进程，每个进程启动的任务不同的情况下需要对任务进行额外分组，下面demo为指定web进程的任务。获取注解方式大部分基于反射实现，属于合规的放心使用。\n\n\n\n```kotlin\n@StartupGroup(group = \"ksp\", strategy = Process.OTHER, processName = [\"web\"])\nclass SimpleTask3 : SimpleStartupTask() {\n\n    override fun run(context: Context) {\n        info(\"SimpleTask3\")\n    }\n}\n\naddMainProcTaskGroup { StartupTaskGroupApplicationKspAll() }\n```\n\n指定主进程任务\n\n```kotlin\n@StartupGroup(group = \"ksp\", strategy = Process.MAIN)\nclass SimpleTask2 : SimpleStartupTask() {\n\n    override fun run(context: Context) {\n        info(\"SimpleTask2\")\n    }\n\n}\n```\n\n3. 添加组，该产物会存在在`build/generated/ksp`文件路径下，并根据group和moduleName等作为唯一类名。\n\n优化项，把所有taskGroup放到了一起，单一module可能只有唯一。\n\n```kotlin\npublic class StartupProcTaskGroupApplicationProc : StartupTaskProcessGroup {\n    public override fun group(builder: Builder, process: String): MutableList\u003cStartupTask\u003e {\n        val list = mutableListOf\u003cStartupTask\u003e()\n        list.add(AsyncTask1())\n        list.add(SimpleTask1())\n        if(process.isMainProc()) {\n            builder.mustAfterAnchorTask(SimpleTask2())\n        }\n        if(process.isMainProc()) {\n            list.add(SampleGenerate1TaskTask())\n        }\n        if(process.contains(\"web\")) {\n            list.add(SimpleTask3())\n        }\n        if(process.contains(\"web\")) {\n            builder.mustAfterAnchorTask(SampleGenerate2TaskTask())\n        }\n        return list\n    }\n}\n\n\naddProcTaskGroup { StartupProcTaskGroupApplicationProc() }\n```\n\n\n# dsl\n\n通过dsl形式动态添加task\n\n构造startup builder ，之后添加任务。 这部分主要就是因为需要构造较多的task函数，看起来不立体了。\n\n以下是dsl版本参考，能力是将简单的任务可以通过dsl的形式添加进去。\n\n```kotlin\nfun Application.createStartup(): Startup.Builder = run {\n    startUp(this) {\n        addTask {\n            simpleTask(\"taskA\") {\n                info(\"taskA\")\n            }\n        }\n        addTask {\n            simpleTask(\"taskB\") {\n                info(\"taskB\")\n            }\n        }\n        addTask {\n            simpleTask(\"taskC\") {\n                info(\"taskC\")\n            }\n        }\n        addTask {\n            simpleTaskBuilder(\"taskD\") {\n                info(\"taskD\")\n            }.apply {\n                dependOn(\"taskC\")\n            }.build()\n        }\n        addTask(\"taskC\") {\n            info(\"taskC\")\n        }\n        setAnchorTask {\n            MyAnchorTask()\n        }\n        addTask {\n            asyncTask(\"asyncTaskA\", {\n                info(\"asyncTaskA\")\n            }, {\n                dependOn(\"asyncTaskD\")\n            })\n        }\n        addAnchorTask {\n            asyncTask(\"asyncTaskB\", {\n                info(\"asyncTaskB\")\n            }, {\n                dependOn(\"asyncTaskA\")\n                await = true\n            })\n        }\n        addAnchorTask {\n            asyncTaskBuilder(\"asyncTaskC\") {\n                info(\"asyncTaskC\")\n                sleep(1000)\n            }.apply {\n                await = true\n                dependOn(\"asyncTaskE\")\n            }.build()\n        }\n        addAnchorTask {\n            asyncTask(\"asyncTaskD\") {\n                info(\"asyncTaskD\")\n                sleep(1000)\n            }\n        }\n        addAnchorTask {\n            asyncTask(\"asyncTaskE\") {\n                info(\"asyncTaskE\")\n                sleep(10000)\n            }\n        }\n        addTaskGroup { taskGroup() }\n        addTaskGroup { StartupTaskGroupApplicationKspMain() }\n        addMainProcTaskGroup { StartupTaskGroupApplicationKspAll() }\n        addProcTaskGroup { StartupProcTaskGroupApplicationKsp() }\n    }\n}\n```\n\n# 锚点任务组\n\n等项目稳定之后，会设立几个锚点任务，后续任务只要挂载到锚点任务之后执行即可，可以简单的设立任务基准。\n\n1.添加锚点任务\n\n```kotlin\nbuildAnchorTask {\n    MyAnchorTask()\n}\n```\n\n2. 挂载mustAfter \n\n```kotlin\n mustAfterAnchor {\n            asyncTask(\"asyncTaskE\") {\n                info(\"asyncTaskE\")\n                sleep(10000)\n            }\n        }\n```\n\n# 调试能力\n\n通过提供额外的调试工程，当启动的所有任务完成后会自动跳转页面并渲染结果，无需任何注册逻辑。\n\n## 使用\n\n```gradle\ndependencies {\n    debugImplementation project(':startup-dag-view')\n}\n```\n\n\u003e 小贴士 不想要该功能则只需 在values下配置如下\n\n```xml\n\n\u003cbool name=\"startup_install_provider_enable\"\u003efalse\u003c/bool\u003e\n```\n\n# ksp\n\n通过ksp 生成一部分动态的组，这部分暂时只能手动添加，后续考虑优先梳理依赖，但是并没有完全完成解耦。\n\n# 调试组件\n\n任务防止劣化的调试工具完成。 \n\n![device-2022-01-02-120141.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3e5d3b85c7334c8d80eee44b4c219cdf~tplv-k3u1fbpfcp-watermark.image?)\n\n## 启动时间轴\n\n江湖上一直流传着我的外号-ui大湿，在下也不是浪得虚名，ui大湿画出来的图形那叫一个美如画啊。\n\n![device-2022-01-02-120203.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/746b475967c947ea83421b954456db94~tplv-k3u1fbpfcp-watermark.image?)\n\n\n这部分原理比较简单，我们把当前启动任务的数据进行了收集，然后根据线程名进行分发，记录任务开始和结束的节点，然后通过图形化进行展示。\n\n如果你第一时间看不懂，可以参考下自选股列表，每一列都是代表一个线程执行的时间轴。\n\n## 启动顺序是否变更\n\n我们会在每次启动的时候将当前启动的顺序进行数据库记录，然后通过数据库找出和当前hashcode不一样的任务，然后比对下用textview的形式展示出来，方便测试同学反馈问题。\n\n这个地方的原理的，我是将整个启动任务通过字符串拼接，然后生成一个字符串，之后通过字符串的hashcode作为唯一标识符，不同字符串生成的hashcode也是不同的。\n\n这里有个傻事就是我一开始对比的是`stringbuilder`的hashcode，然后发现一样的任务竟然值变更了，我真傻真的。\n\n![device-2022-01-02-120221.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ba1f718ec1794140bd53306f351ae782~tplv-k3u1fbpfcp-watermark.image?)\n\n别问，问就是ui大湿，textview不香？\n\n\n# 玩具属性\n\n因为是玩具工程，仅供大家学习了，有问题可以直接联系我，联系方式如下\n\n\u003e qq: 454327998\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleifzhang%2Fandroidstartup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleifzhang%2Fandroidstartup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleifzhang%2Fandroidstartup/lists"}