GitHub标星9

它包含 4 个层次:

主工程:除了一些全局配置和主 Activity 之外,不包含任何业务代码。

业务组件:最上层的业务,每个组件表示一条完整的业务线,彼此之间互相独立。

基础组件:支撑上层业务组件运行的基础业务服务。

基础 SDK:完全业务无关的基础代码。

各层次职责清晰独立,可以很方便的进行拆解和组合;由于都有自己的版本,业务线可以独立发版,随时升级、回滚。

基本解耦方案

组件化的第一步就是对要拆出去的组件进行解耦,常见解耦方式有以下几种:

(1) 公用代码处理

  1. 基础业务逻辑分别拆成基础组件

  2. 自身逻辑完整、用于完成某一特定功能、不含业务逻辑的一组代码,独立成 SDK

  3. 代码量很小不足以拆分成单独拆分的代码和资源,我们统一放在一个专门建立的 common 组件中,并且严格限制 common 组件的增长。随着组件化的逐渐进行,common 应该逐渐变小而不是增大。

  4. 碰巧被共同使用的一些代码和资源片段,通常它们被复用只是因为被开发人员搜索到而直接使用了,很多时候某个资源已经被 A 业务声明了前缀,但是由于没有隔离,仍然会不可避免的被他人在 B 业务中强行复用,这时候如果 A 业务方要进行一些修改,B 业务就会受到影响 —— 这种情况我们允许直接复制

(2) 初始化

有些组件有在应用启动时初始化服务的需求,而且很多服务还是有依赖关系的,最初我们为每个组件都添加了一个 init() 方法,但是并不能解决依赖顺序问题,需要每个组件都在 app 工程中按顺序添加初始化代码才能正常运行,这使得不熟悉整套组件业务的人很难建立起一个可以独立运行的组件 app。因此我们开发了一套多线程初始化框架,每个组件只要新建若干个启动 Task 类,并在 Task 中声明依赖关系即可:

App 只需知道自己依赖什么组件即可,在编译 app 时 gradle 插件会自动定位到所有的 Task,并运行时生成依赖图,按依赖顺序启动 Task:

这样就解决了组件在主工程中堆积初始化代码的问题,在简化了代码的同时还有加快启动速度的功效。

(3) 路由

界面间使用 Url 进行跳转,不但实现了解耦,也统一了各端的页面打开方式。我们实现了一套灵活小巧的路由框架 ZRouter,它支持多组件、路由拦截、AB Test 、参数正则匹配、降级策略、任意参数传递以及自定义跳转等功能,可以自定义路由的各个阶段,完全满足了我们的业务需求。

(4) 接口

除了页面间的跳转,不同业务之间不可避免的会有一些调用,为了避免组件的直接通信,通常都是使用接口依赖的方式。我们实现了一个 Interface Provider 来支持接口通信,它可以通过运行时在动态注册一个接口,同时也实现了对于 ServiceLoader 的支持。只要一方组件将通信接口暴露出来,使用方就可以直接使用接口进行调用。

动态注册接口

Provider.register(AbcInterface.class,new AbcInterfaceImpl())

获取实例并调用

Provider.get(AbcInterface.class).doSomething()

(5) EventBus

这个自不必说,虽然说滥用是一个问题,但是有些场景下,使用事件还是最为方便简单的方式

(6) 组件 API 模块

上面提到的接口和事件以及一些跨组件使用的 Model 放到哪里好呢?如果直接将这些类下沉到一个公共组件中,由于业务的频繁更新,这个公共组件可能会更新得十分频繁,开发也十分的不方便,所以使用公共组件是行不通的,于是我们采取了另一种方式——组件 API :为每个有对外暴露需求的组件添加一个 API 模块,API 模块中只包含对外暴露的 Model 和组件通信用的 Interface 与 Event。有需要引用这些类的组件只要依赖 API 即可。

[图片上传中…(image-a39cd0-1598345691997-6)]

一个典型的组件工程结构是这个样子:

以上图为例,它包含三个模块:

template :组件代码,它包含了这个组件所有业务代码

template-api:组件的接口模块,专门用于与其他组件通信,只包含 Model、Interface 和 Event,不存在任何业务和逻辑代码

app 模块:用于独立运行 app,它直接依赖组件模块,只要添加一些简单的配置,即可实现组件独立运行。

组件半自动拆分

有了解耦的方法,剩下的就是采取行动拆分组件了,拆组件是一个很头疼的问题,它非常考虑一个人的细心与耐心,由于无法准确知道有哪些代码要被拆走,也不能直观的知晓依赖关系,移动变得非常的困难且容易出错,一旦不能一次性拆分成功,到处都是编译错误,便只能靠人肉一点一点的挪。

工欲善其事,必先利其器。为了解决这个问题,我们开发了一个辅助工具 RefactorMan: 它可以递归的解析出工程中所有源码的引用和被引用情况,同时会根据预设规则自动分析出所有不合理的依赖,在开发人员根据提示解决了不合理依赖之后,即可将组件一键移出,大大减少了拆组件的工作量。我们在组件化初期曾经走过一些弯路,最初拆出的八个组件工程的的部分源码经历了几次的反复移动才得出最优解,而有了 RefactorMan,我们可以面对反复的拆分和组合组件有恃无恐。

Bonus :由于可以分析和移动资源,所以额外获得了清理无用资源的功能。

联合编译完整包

单独运行组件 app 并不能完整的覆盖所有的 case,尤其是在给 QA 测试的时候,还是需要编译完整的主工程包的,所以我们需要一个直接编译完整包的方案:

最初我们的实现方式只针对组件,比较简单:

首先在 setting.gradle 中动态引入组件 module:

def allComponents = [“base”, “account” … “template” …]

allComponents.forEach({ name ->

if (shouldUseSource(name)) {

// 动态引入外部模块

include “😒{name}”

project(“😒{name}”).projectDir = getComponentDir(name);

}

})

然后在 app/build.gradle 中切换依赖,需要将所有被间接依赖的组件全部 exclude 以防止同时依赖了一个组件的 module 和 aar:

allComponents.forEach({ name ->

if (shouldUseSource(name)) {

implementation(project(“😒{name}”)) { exclude group: COMPONENT_GROUP }

} else {

implementation(“ C O M P O N E N T G R O U P : {COMPONENT_GROUP}: COMPONENTGROUP:{name}😒{versions[name]}”) { exclude group: COMPONENT_GROUP }

}

})

由于所有组件的 group 都是一样的,所以这样做并没有什么问题,但是后来一些基础 SDK 也出现了这种需求,这时候就需要一种通用的源码依赖方案,因此做了一下修改,直接使用 gradle 提供的依赖替换功能,只需要修改 setting.gradle 即可:

// … 忽略读取配置代码 …

configs.forEach { artifact, prj ->

include “😒{prj.name}”

project(“😒{prj.name}”).projectDir = new File(prj.dir)

}

gradle.allprojects { project ->

if (project == project.rootProject) {

return

}

project.configurations.all {

resolutionStrategy.dependencySubstitution {

configs.forEach { artifact, prj ->

// 在这里进行替换

substitute module(artifact) with project(“😒{prj.name}”)

}

}

}

}

而 build.gradle 的依赖写法与普通的工程完全一样。

普通状态下的的主工程:

源码引用 template 组件后的主工程:

这样我们就可以像之前在单工程中一样写代码了。

得益于源码引用,我们直接在提交组件代码的时候,CI 会自动联合主工程编译出完整包,QA 会根据完整包进行测试,在测试通过后即可自动发布到公司的仓库,并通过内部的集成平台集成到主工程

小 tip :工程 .idea/vcs.xml 中定义了当前工程关联的 Git 仓库,可以在联合编译的同时通过修改 vcs.xml 来把组件目录也关联到主工程 Git 配置中,在开发过程中就可以使用 Android Studio 的内置 Git 功能了

包含子业务线的组件

我们当前的组件,绝大部分是一个组件一个仓库的,对于一般的组件来说,并没有什么问题,但是对于有的业务线,本身规模比较大,包含了若干个子业务,比如知乎大学,电子书、live 和私家课等子业务,这些子业务本身功能独立,但是共享整个业务线的基础代码,同时大业务线也有会一些汇总所有子业务的页面,它们的关系是这个样子:

这几个业务如果都要拆分出去独立成组件,然后抽离公共部分成为也成为一个业务线基础组件,这时候会面临一个很大的问题:由于几条业务线都属于同一个主业务线,做活动或者上新 Feature 的时候,这几个组件经常会发生联动,需要先更新 base 再更新其他业务线,提交 mr 也要同时提多个仓库,出现频繁的连锁更新;而如果不拆的话,业务线代码本身就已经很庞大,即使是单独编译组件 app 也会很慢,并且随着时间的推移,各个业务线的代码边界会像组件化之前的主工程一样逐渐劣化,耦合会越来越严重。

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: GitHub标星 115k 是指某个项目在 GitHub 上收到了 11.5 万个星标(Star)。星标是 GitHub 上用户对项目的一种点赞行为,表示对项目的认可和喜爱。 当一个项目被很多用户标星的时候,通常表示这个项目具有一定的影响力,受到了广泛的关注和认可。标星的数量可以作为评估一个项目受欢迎程度的指标之一。 标星数量的增加主要有以下几个原因: 1. 项目本身优秀:如果一个项目具有很高的代码质量、功能完善、易于使用等优点,就会吸引更多的开发者关注并标星。 2. 开发者社区推广:开发者社区对于一个项目的推广非常重要,如果一个项目在社区中得到了推荐和宣传,就会吸引更多的用户关注和标星。 3. 开发者贡献:开源项目常常接受开发者的贡献,如果一个项目有很多活跃的开发者,意味着项目会持续发展和更新,这也会吸引更多的用户关注和标星。 4. 开发者口碑和用户反馈:如果一个项目具有良好的口碑和用户反馈,证明这个项目对用户来说是有价值的,也会吸引更多的用户关注和标星GitHub标星 115k 表明这个项目受到了极高的关注和认可。这种关注既可以是因为项目本身的优秀,也可能是因为开发者社区的推广和用户口碑的效应。不过,标星数量并不是唯一的评判标准,我们还需要进一步了解项目的质量、活跃度和用户评价等方面的信息,才能全面评估一个项目的价值。 ### 回答2: GitHub 是全球最大的开源代码托管平台之一,用户可以在平台上发布开源项目,并且其他用户可以对其进行关注(Star)。标星(Star)是 GitHub 上一项重要的功能,用于表示用户对某个项目的喜爱程度或兴趣程度。 在你提到的这个情况中,一个项目在 GitHub标星达到 115k 的数量是非常惊人的。这意味着该项目受到了非常广泛的关注和认可,吸引了大量的用户关注和参与。 标星数量的增长通常表明该项目具有出色的代码质量、创新的想法、丰富的功能或对开发者社区有重要的影响。标星数量还能够作为用户评估一个项目质量和受欢迎程度的参考指标。越多的标星数量通常意味着该项目受到更多人的信任和广泛的使用。 标星 115k 的项目往往是在技术方面具有重要意义的项目,也可能是在开发工具、库或框架上具有重要创新的项目。当项目受到如此高的关注时,它不仅会吸引更多的开发者参与贡献代码,也会吸引更多的用户试用和提供反馈。 通过标星数量,我们可以得知这个项目在社区的影响力和认可度。这使得其他开发者能够了解或了解到一些有趣或有价值的项目。同时,对于该项目的维护者来说,标星数量也是对他们工作的认可和鞭策,鼓励他们继续努力改进和维护该项目。 综上所述,GitHub 上一个标星数量达到 115k 的项目无疑是一个备受关注和认可的项目,代表着该项目的质量和影响力。这个数量反映了开发者社区对该项目的认可和信赖程度,证明了该项目在技术领域的重要地位。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值