前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >微信Android模块化架构重构实践

微信Android模块化架构重构实践

作者头像
腾讯Bugly
发布于 2023-04-02 06:13:45
发布于 2023-04-02 06:13:45
1.1K0
举报
文章被收录于专栏:腾讯Bugly的专栏腾讯Bugly的专栏

本文来自:微信移动客户端开发团队公众号(WeMobileDev)

微信Android架构历史

微信Android诞生之初,用的是常见的分层结构设计。这种架构简单、清晰并一直沿袭至今。这是微信架构的v1.x时代。

图1-架构演进

到了微信架构的v2.x时代,随着业务的快速发展,消息通知不及时和Android 2.3版本之前webview内存泄露问题开始突显。由于代码、内存、apk大小都在增长,对系统资源的占用越来越多,导致微信进程容易被系统回收。因此微信开始转向多进程架构,独立的通信进程保持长连接的稳定性,独立的webview进程也阻隔了内存泄露导致的问题。

时间继续推进,我们也遇到了65535问题和LinearAlloc问题。这时的微信已经具备了许多功能像朋友圈、摇一摇、附近的人等等,分离核心功能和其他业务模块变得越发重要。为此,微信开启了第三次架构改造(v3.x)。我们对各种产品功能进行解耦并拆分到相互独立的p_xxx工程中,这是微信第一次进行模块化架构的重构。经过几个月的努力,微信拆出了几十个p工程,它们都通过基础组件访问网络、存储等服务,互相独立并行。新的p工程架构支撑了微信更快速的业务发展,配合多分支开发模式的改进,能够支持团队多分支多team的并行开发。

图2 - 架构图

为何再次重构微信

原本好好的架构出了什么问题?

从上个架构之后的两年多时间里,微信Android基本没有大的架构改动。配合gradle的编译,以及git的多分支并行开发,微信的模块工程数量不断增多,支撑了游戏、支付等大功能,可以说这段时间里原有架构起到了很好的作用。

然而随着代码继续膨胀,一些问题开始突显出来。首先出问题的是基础工程libnetscene和libplugin。基础工程一直处于不断膨胀的状态,同时主工程也在不断变大。同时基础工程存在中心化问题,许多业务Storage类被附着在一个核心类上面,久而久之这个类已经没法看了。此外当初为了平滑切换到gradle避免结构变化太大以及太多module,我们将所有工程都对接到一个module上。缺少了编译上的隔离,模块间的代码边界出现一些劣化。虽然紧接着开发了工具来限制模块间的错误依赖,但这段时间里的影响已经产生。在上面各种问题之下,许多模块已经称不上“独立”了。所以当我们重新审视代码架构时,以前良好模块化的架构设计已经逐渐变了样。

图3 - 架构逐渐的变化

“君有疾在腠理,不治将恐深”,在我们还在犹豫到底要不要重构的时候,硬件同学向我们提出了需求。希望将微信Android代码移植到类似微信相册这样产品中。这样就可以快速跟进微信业务最新的支撑组件、协议、安全性、后台服务等能力,而且代码要尽可能精简,可以选择和定制模块,可以移植模块来实现原型尝试。但就之前的情况来说,微信一时难以满足。这下定了,还得重构。

于是我们回过头仔细看之前的设计,找找问题究竟是怎么来的。

问题出在哪

先寻找代码膨胀的原因。

翻开基础工程的代码,我们看到除了符合设计初衷的存储、网络等支持组件外,还有相当多的业务相关代码。这些代码是膨胀的来源。但代码怎么来的,非要放这?一切不合理皆有背后的逻辑。在之前的架构中,我们大量适用Event事件总线作为模块间通信的方式,也基本是唯一的方式。使用Event作为通信的媒介,自然要有定义它的地方,好让模块之间都能知道Event结构是怎样的。这时候基础工程好像就成了存放Event的唯一选择——Event定义被放在基础工程中;接着,遇到某个模块A想使用模块B的数据结构类,怎么办?把类下沉到基础工程;遇到模块A想用模块B的某个接口返回个数据,Event好像不太适合?那就把代码下沉到基础工程吧……

就这样越来越多的代码很“自然的”被下沉到基础工程中。

我们再看看主工程,它膨胀的原因不一样。分析一下基本能确定的是,首先作为主干业务一直还有需求在开发,膨胀在所难免,缺少适当的内部重构但暂时不是问题的核心。另一部分原因,则是因为模块的生命周期设计好像已经不满足使用需要。之前的模块生命周期是从“Account初始化”到“Account已注销”,所以可以看出在这时机之外肯定还有逻辑。放在以前这不是个大问题,刚启动还不等“Account初始化”就要执行的逻辑哪有那么多。而现在不一样,再简单的逻辑堆积起来也会变复杂。此时,在模块生命周期外的逻辑基本上只能放主工程。

此外的问题,模块边界破坏、基础工程中心化,都是代码持续劣化的帮凶。

总之在模块化上我们忽视了一些重要的问题,必须重塑。

重塑模块化

重塑模块化,我们分解为三个目标:

  • 改变通信方式
  • 重新设计模块
  • 约束代码边界

改变通信方式

前面讲过,我们使用Event总线作为模块间通信的媒介,这种设计很常见。然而当回顾整体代码时能发现,Event并非所有通信需要的最佳形式。它的特点适合一对多的广播场景,依赖关系弱。一旦遇到需要一组业务接口时,用Event写起来那是十分痛苦的。也正因如此,这种情况下大家都跳过了Event的使用,直接将代码下沉到了基础工程,共享代码,进而导致基础工程的不断膨胀。

所以选个合适的通信方式很有必要,我们希望兼顾考虑开发的便利性和协议的约束性。

Event不合适。协议通信如何?

我们理解的协议通信,是指跨平台/序列化的通信方式,类似终端和服务器间的通信或restful这种。现在这种形式在终端内很常见了。协议通信具备一种很强力解耦能力,但也有不可忽视的代价。无论什么形式的通信,所有的协议定义需要让通讯两方都能获知。通常为了方便会在某个公共区域存放所有协议的定义,这情况和Event引发的问题有点像。另外,协议如果变化了,两端怎么同步就变得有点复杂,至少要配合一些框架来实现。在一个应用内,这样会不会有点复杂?用起来好像也不那么方便?更何况它究竟解决多少问题呢。

所以我们想要简单点。经过权衡,我们决定用模块提供“SDK”的方式作为它与其他模块进行通信的手段。

通常“SDK”提供的是什么,是接口 + 数据结构。这种方式好处明显:实现简单也能解决问题,IDE容易补全、调用接口方便,不用配合工具,协议变化直接反映在编译上,维护接口也简单了。

其实想想,用协议的方式在终端内作为通信手段,开发效率低,也容易出错。因此可能会诞生各种框架和工具来提升这里损失的效率。到头来,是不是大家都实现了一套类似RPC这样的封装。其实本地的通信,能用接口就挺好,不能用的时候,再用协议封装也来得及。

确定了方案,实现起来就很简单。我们的注册方式和接口访问都很简单。用接口注册,再用接口访问,不暴露实现细节。如下图。

图4 - 注册接口

图5 - 访问接口

接下来,怎么暴露接口更方便?

模块暴露“SDK”的方式无非就是新建个“SDK”工程,剥离接口和数据结构到该工程里面,然后让其他模块引用编译。但这样有点麻烦,能不能再方便点?

当然有办法。我们实现了另一种接口暴露的形式——“.api化”。

使用方式和思路都很简单。对于java文件,将工程里想要暴露出去的接口类后缀名从“.java”改成“.api”,就可以了。

而且并不只是java文件,其他文件如果也想暴露,在文件名后增加".api”,也一样可以。

图6 - “.api化”

当然,要让工程支持这样的方式,gradle文件肯定会有一点改变。

settings.gradle

build.gradle

图 7 - settings.gradle & build.gradle

就这样,可以说暴露接口变得非常容易,不用担心实现类也被人引用到。而它的实现原理也相当简单:自动生成一个“SDK”工程,拷贝.api后缀文件到工程中就行了,后面其他工程依赖编译的只是这个生成的工程。简单好用。

还有个细节,如果想编辑.api后缀的java文件,为了能让Android Studio继续高亮该怎么办?可以在File Type中把.api作为java文件类型。

图8 - 设置File Types

重新设计模块

要把模块重新设计,还要做好几件事。首先,消灭代码经常下沉的“三不管区域”——基础工程。这意味着原来的模块要把之前下沉的代码重新认领回去。

图9 - 分层结构改造

为了巩固替代基础工程的mmkernel层,不被滥用为新的代码堆放处,顺便还要解决中心化问题。就必须强化它的职责和设计。

mmkernel结构可以很通用的定义为CoreAccount/CoreNetwork/CoreStorage三个部分,分别提供了核心账号状态(初始化、注销)、网络状态回调(链接建立)、存储状态生命周期(db创建、销毁、用户存储路径切换、sdcard挂起)。 

图10 

再然后是生命周期问题,我们需要重新设计正确的生命周期。

之前讲过,我们的模块生命周期大体上只有“Account初始化”和“Account注销”两个阶段。这已经不够用了。

所以扩大模块的生命周期,就给了模块实现各种代码需要的时机,才能避免大家往主工程塞代码。

图11

实现新的生命周期是一个正确的选择,同时产生了解决另一个问题的机会——复杂的启动流程。

要知道主工程的代码一部分原因是启动流程堆积造成的,逻辑多了代码自然多。随之而来的问题就是代码多了,逻辑也就跟着复杂起来。微信的初始化逻辑是顺序排列在一起并从上到下执行,某种情况下还会异步启动。当程序启动流程比较复杂时,这样的代码会产生“隐性依赖”的问题。“隐性依赖”顾名思义就是:原本并应该存在依赖的代码,随着版本的迭代逐渐产生了依赖,而且还不明显。这样的情况会让情况恶化,大家只敢往里面堆代码,但却不敢“乱动”。

所以重新设计的模块应该要彻底避免这些问题。

我们重新定义了模块的生命周期,将模块的生命周期延长到应用启动和退出。而后,每个模块都可以定义一个Plugin类,作为模块的“支柱”或“起点”。作为解决初始化问题的手段,它具备几个主要阶段:dependency()configure()execute()

图12 - plugin初始化的几个阶段

dependency()阶段,用于设置需要依赖的其他Plugin,当然提供那个Plugin的别名接口类就可以了。

图13 - 设置dependency

依赖阶段我们会生成整个模块的依赖树。这与编译时的依赖不同。通常的依赖关系是分为两种的,一种是类型依赖也就是编译期依赖,需要被依赖模块提供具体类型才能编译通过;另一种依赖则是运行期的逻辑依赖或数据一致性依赖,当一个模块用这种方式依赖另一个模块,就意味着,前者的执行要依赖后者执行已完成,通常是为了数据准备妥当或保证所需服务已被注册。

显性的运行期依赖把之前启动逻辑的“隐性依赖”完全暴露在阳光之下,改启动逻辑不用提心吊胆。

图14 - 依赖关系树状图

configure()阶段,该阶段是根据之前的依赖树遍历执行。通常用于初始化一些数据配置、注册IService服务、向前面依赖的模块注册一些回调等等。

此外这个阶段还有额外的作用是插入BootTask,用于后面execute()阶段的执行。

图15 - configure阶段

execute()阶段,为了改变启动流程不清楚的情况,强调启动逻辑之间的依赖关系,我们现在将每个要执行的启动步骤封装为BootTask。前面的configure阶段时,我们可以将BootTask插入到通过dependency()得到的依赖树。每个Plugin同时也是一个BootTask,也因此拥有execute()接口。最终得到了包含所有BootTask的启动树,将遍历执行所有节点执行execute()。

图16 - BootTask

独立使用BootTask的方式并不十分常见,通常Plugin本身的execute已经够用。不过在一些通用型组件初始化尝试会需要用到,如某些给某个全局使用的预加载资源提前初始化的逻辑。

为何设计configure()和execute(),这可以理解为“收集任务”和“执行任务”的两个阶段。另外这样的抽象还可以实现从外部调度execute的执行线程,将启动逻辑和启动异步代码分开。顺便解决了,原来异步启动代码混乱不堪的情况。

约束代码边界

从之前的经验看,要想约束好代码的边界不被破坏,编译上的隔离是唯一法宝。

除了工程和工程之间的分割,在工程的内部如果也能实现约束代码就更好了,算是将问题扼杀在摇篮里。之前通常的做法是以module工程为单位的相互分离,但在工程内部并不限制代码相互引用。所以为了规范代码,常能看到用包名作为约定,区分内部功能职责,靠约定维持解耦。随着时间推移,很快就能发现包名约定作为约束太弱了,在快速迭代的代码上很难一直维持下去。不管怎么样解决,总要通过一些手段审查代码引用的对不对。感觉有点防不胜防。为此,我们实现了一种简单易用、粒度更细的工程组织结构——pins工程结构

图17 - pins工程示例图

使用pins工程结构,我们考虑的是要能对接兼容以前结构下那么多伪工程,又能在开始设计某个业务模块时,能用极小粒度的代码解耦,限制错误引用,以防后患。

pins工程能在module之内再次构建完整的多子工程结构,通过project.properties来指定编译依赖关系。通过依赖关系在编译时找到所有的资源和源码路径。而对边界的约束需要配合code-check工具在编译期进行检查,杜绝依赖关系之外的代码引用。

图18 - code-check

这样的工程组织形式的两个明显好处:

  • 约束代码粒度和小代码边界的利器    

粒度极小,一个pin工程也许只有一个源文件,只要它能表达一个独立职责。对于任何一个模块,从内部约束自己的功能结构,是对整体代码边界约束的极大补充。以前面插的结构为例,一个gallery业务可能提供了几种不同的产品功能,以及支撑能力。那么将其相互独立的代码进行区分,避免混杂,就会显得十分必要。清晰的结构,意味着后期维护成本的降低和开发效率的提高,留下了灵活性。

  • 避免的超量module的创建,轻量

pins工程某种程度上能减少一些粒度太小的module工程,也一定程度的缓解太多module工程时的gradle编译性能问题。

至此,我们基本完成了重塑模块化的设计目标,解决掉很多之前没有考虑的问题。算是模块化的加强版。另外设计是一方面,拆分解耦原来代码以及迁移还是另一回事,这个过程也是十分艰难和枯燥这里就不细讲了。接下来想办法看看重构的效果。

看看效果

重新设计的模块化加上代码的重构。我们终于能满足之前硬件同学的需要。同时一并解决许多拖欠的问题。

在编译上,整体编译速度会因为module增多而下降一些。但拆分module之后,却能显著加快单工程增量编译的速度。和之前相比,一行代码的增量编译耗时能减少60%。

除了满足需求外,架构设计的效果并不好量化,不过我们尝试用一个demo来说明。

WeChat nano

基于前面介绍过的轻量的微信内核mmkernel层,再配合一个不包含界面的基础聊天模块和Auth模块,可以在短时间里开发出一个及精简版本的微信——WeChat nano。

图19 - WeChat nano

模拟这个console的界面是单独开发的,时间的大头都花在这上面。

它的效果不错:

  • 可以让安装包大小缩减到3.5M,大概是完整版本的10%
  • 能大幅减小内存占用,约占用完整版本的25% (注:只计算应用相关有不同的部分PSS) 

大概就是这样。

取舍和选择

对于架构重构,我们也曾放眼行业内已经发布过的各种方案,希望从中找到一些解决思路。

我们参考了很多业界开放和发表的架构设计。总的来说,目前Android端App整体架设计上,除了聚焦在“大前端”之外,基本上都在“插件化/应用沙盒”上面下功夫。可以参考如atlas、small、DroidPlugin、DynamicApk等等方案,不难发现让模块最终具备动态性是它们最核心的能力。

“大前端”是个热门的方向。我们结合自身情况,考虑到迁移已有代码不是一件容易事,让团队彻底切换编程工具短期不现实。这种“远水解不了近渴”的感觉,也只能暂且作罢。

我们把目光投向了“插件化/应用沙盒”。沙盒的好处是它具备很强的隔离性,有些方案可以彻底隔离代码和资源,边界限制性极强,效果很好。此外,多数方案也都具备动态更新能力、补丁能力,动态性基本成为标配。所以这时我们在考虑需不需要走上这样的路。

一切从自身情况出发。在微信的角度看,我们最关心的问题是如何能约束住代码边界,如何防止架构劣化,如何提高开发效率。这样的情况下,重新审视了具备动态性的插件化和沙盒方案。先从动态性上考虑,回看业内的使用经验,目前的动态性通常有两个主要的用途:1)作为热补丁使用;2)业务并行发布需求强烈,例如运营需求,作为快速发布的手段。对于第二点,就目前的微信团队来说是较少遇到的弱需求。相较之下,工具类和电商类应用常有符合的使用场景。而对于第一点,我们则期望在实现模块化上,目标相对纯粹一些,专心解决代码自身问题,之后再通过tinker在开发阶段之外实现热补丁上的动态性需要。

再来看隔离性。需要说明的是,这里的隔离型主要来自于沙盒架构效果。对于非独立插件化工程(需要编译依赖其他插件)除了动态性,它的作用和普通module在代码隔离上没有区别,不做讨论。

我们心里很期盼代码与代码之间那种干净清爽的分割效果,但事与愿违。

图20 - 模块依赖示意图

从上面这张模块依赖示意图看到,微信业务模块之间数据关系相当复杂,模块间相互访问数据、共享某些功能的行为如此普遍。而实际情况比示意图更麻烦。

面对微信业务数据相互间频繁的调用,沙盒隔离容易导致代码复用困难和相互调用麻烦,想在微信上实现,目前困难极大。重构难度不谈,单是隔离的收益就很有可能无法弥补开发效率上的损失。

从开发模式上看也是一样。微信Android团队目前每个迭代大概三四十人参与,内部沟通成本不算高到不可接受。通常开发同学可能要同时开发和修改几个模块并保证他们相互模块独立,同时又可能有频繁的模块间通信。这种时候,模块调用方不方便显得很重要。

此外还需要考虑的问题,从几个成熟方案中都能看到hook Android框架、修改aapt、替换或包装android gradle plugin、代理组件等等设计。这些方案的复杂度和兼容性代价,不能忽视。使用和维护它们需要仔细权衡。

所以思前想后我们仍选择了重走最开始的模块化之路。一切问题的根本还是在那个说的很多年的代码边界、解耦和内聚上。只要有了清晰独立的代码模块,才有将来其他变化的可能性。

代码之外,架构之内

模块负责人制度

有这样一句话,“不被监管的权利一定会发生腐败” 。如果放到软件开发的行当来说,就是“不被监管的代码也一定会发生劣化”。所以代码应该要接受“监管”。

为了能长期有效的保持代码质量,我们开始执行新的代码审查机制——模块负责人制度。

代码审查的好处毋庸置疑。在这之前,微信由于业务发展快速,同学们经常会变换需求的开发方向。面对着业务模块数量比人多的情况,开发同学经常一个人需要开发多个模块。也因此许多模块被无数人维护过,基础的支撑工程更是如此。这种类似游击战的方式,开发效率很高,支撑了微信快速的研发节奏,但也导致了“无主代码”特别多。大家缺少对代码的“归属感”,也降低了改进优化模块的欲望。

另外在这之前,代码审查是由leader对申请回流主干的Merge Request进行review,这导致效率较低且容易遗漏问题。合理的代码审查更应该是全员性质的。

模块负责人制度尝试改变这些现状。通过大家认领模块,对模块的代码和设计负责,对模块对外提供的接口服务负责,对其他人修改自己模块的行为进行监督。这些情况明显提高开发同学的代码所有感,改变大家修改优化和修改代码的动机。

推动模块负责人制度,渐进式的推动了大范围的代码审查,这样的方式很适合像微信这样没有从一开始执行全员性质Code Review的项目。目前模块负责人机制运转顺利,代码审查率和模块认领率都在提高。

重构与开发者心态的关系

在一个长期没有改进的框架下,开发者的习惯可能会逐步变成跟随式、保守式的开发。这大概可以被描述成“只要别人这样做,我也这样做,哪怕这么样的设计不好,但也不会错”。随着心态逐渐普遍,另一种情形出现:经常能听到有同学吐槽一些代码,却更少看到代码在被改进。这说明一些沉积的问题不是没有被大家发现,只是没有人愿意去修改。这种情况下代码和框架会随着时间变得越来越差,有些问题逐渐变成“陈年旧病”。 面对这个问题首先要说,这不是开发者合格与否的问题,实际上有想法的开发人员有很多,但想将每个想法转换成代码并让大家接受,并不是一件很容易的事。尤其在一个大框架下,尝试改变的代价很大。如果他的主要任务不在改进某些模块上,那么很多想法最后都无法变成现实。这也是为什么保守和跟随的习惯会逐渐变的普遍。

保守的气氛需要被打破。当开启一次重构之后,你会发现团队中会有很多积极的声音响应,他们会把积压的想法和意见抛出来。一次问题的解决,可能会为另一个问题的解决带来机会,其他开发同学的一些想法也许就能更容易落地。所以不定期的推动一些模块的重构,将一些对代码的不满释放出来,是一种不错的激活。

此外在重构之后,还要考虑引导开发的代码组织方式切换,多用模板、正确的代码实例等,让他们可以放心参考。

模块划分经验谈

维持代码边界

代码的边界就像一堵墙,架构的劣化都是从这堵墙的瓦解开始的。从以往的经验来看,编译上的隔离是最好的约束手段,单纯的约定或准则并不能永远的保持下去。 所以在任何情况下都尽可能不要放开编译上的约束。接着,将接口和实现分离,其他工程只依赖接口而不依赖实现,这样的边界效果更好。当然破坏无处不在,例如遇到某个紧急需求要某模块新增若干接口,就可能出现跳过接口直接依赖实现工程进行开发的情况。这时可以考虑通过代码的审查进行监督,也可以通过开发简单的编译脚本,检查是否有不当依赖产生。

划定模块边界的细节问题

当对代码进行解耦时,即便大体上的模块职责划分已经清晰,但因为模块间的各种业务关系,细节上仍会遇到纠缠不清的情况。事实上,因为需求及功能的不同,并没有哪一种模块划分的规则是完全适用于每个应用的。随着业务的发展和变化,模块边界出现不合适的情况完全符合预期。

那么如何让模块划分更让大家觉得合理,或者说当遇到一个两难选择时,按照什么样的方式大家会更好理解?我们建议的方法其实也很简单:试着对代码“讲一个符合逻辑的故事”,哪个故事讲得通,你就可以将之作为拆分的选择。因为代码解耦从来不是问题,纠结的只是解耦行为能不能让人理解。例如一些模块间通信用的数据结构究竟属于那个模块的问题就可以用这种方式仲裁。在纠结的时候,能自圆其说的方案往往就足够了。我们要尽力避免的,应该是随意拼凑和单纯为了类型解耦而解耦的情况。

模块的一般组织方式 

设计一个模块,我们有一个一般性的组织方式,可以将模块分成三个工程:implementation工程、api工程、library工程。

implementation工程提供逻辑的实现。api工程提供对外的接口和数据结构。library工程,则提供该模块的一些工具类。

从另一个角度看,implementation工程实际上是和应用的状态、生命周期相关的,它的执行依赖于各种应用状态。而library工程则不关心这些状态。因此也可以看做library提供某种功能,implementation则是如何运用这种功能。例如,我们实现一个表情模块,library工程提供表情的资源、表情的渲染和播放能力,api工程提供了使用表情的服务接口,implementation工程则提供了api的实现,及何时开始加载表情资源、缓存管理、以及其他表情功能例如商店等等。

当然,这是一个指导性的建议,很多时候library工程和api工程之间没有明显边界也很正常。但强烈建议至少要有implementation工程和api工程。

分析依赖关系的工具

解耦代码时,快速分析代码的依赖关系能很好的提升工作效率。Android Studio提供了一个不错的工具。

图21 - Analyze dependency工具

文件、资源以及工程,都可以进行依赖分析。有了分析结果,接下来一步一步把代码分离就简单多了。

最后

重构整体架构不是一件容易事,通常也不太可能让整个团队停下来只做重构。所以一直以来微信的重构都是随着版本迭代进行“拆分”-> “灰度” -> “回流”的循环节奏。

“设计系统的组织,其产生的设计和架构等价于组织间的沟通结构”。对于微信几年间走过的路程,时至今日团队内的沟通形式还可以做到较多的直接沟通。这些情况决定了微信如今的技术选择。所以在方案选择上我们就更愿意寻求相对简单合适的方式解决问题——用纯粹的模块化保持后续架构的灵活性和健壮性,重新强调依赖、强调应用状态和生命周期、强化代码的边界。

除了代码上的设计,代码之外我们也做了些努力。我们认同代码审查的意义,也开始推行模块负责人的审查机制。此外我们还打算强化文档的使用,当然这个还在规划中。

最后希望我们分享的一点经验能对大家有些价值,欢迎留言交流 :)


如果您觉得我们的内容还不错,就请转发到朋友圈,和小伙伴一起分享吧~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-07-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯Bugly 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
微信 Android 模块化架构重构实践(上)
微信终端开发团队
2017/08/07
11.3K15
微信 Android 模块化架构重构实践(上)
微信 Android 模块化架构重构实践(下)
该文介绍了微信Android客户端在代码架构上的优化过程,包括模块化、组件化以及代码分析等方面。优化后的代码架构更利于团队协作,提高了开发效率。同时,也探讨了代码架构的优化对代码质量的影响。
微信终端开发团队
2017/08/08
4.8K0
微信 Android 模块化架构重构实践(下)
干货 | Android工程模块化平台的设计
作者简介 张涛,饿了么资深Android工程师,“开源实验室”博主,Kotlin 技术推广者。2013年开始从事Android开发,带过团队,做过架构,写过应用,做过开源社区。目前在饿了么商户端负责应
携程技术
2018/07/05
1.2K0
Android工程模块化平台的设计
大家好,今天跟大家分享的主题是《Android工程模块化平台的设计》 首先自我介绍一下:我叫张涛,目前就职于饿了么移动技术部。可能有些朋友认识我,我之前也会在我博客【开源实验室】写一些Android相关的技术点。 今天我们讲的主题是基于项目模块化来说的,模块化是什么大家肯定都是知道了的,这里问一下大家,有多少人在此之前有做过模块化的,举个手我看一下;了解过听说过模块化的呢?这次比较多。 我们说,做模块化其实跟项目重构很像,都是从这几个点来做的,只是侧重点不同。分别是:删除、组织、降级、解耦。那么这四点是什么
用户1907613
2018/07/20
3920
货拉拉 Android 模块化路由框架:TheRouter
TheRouter 是一个 Kotlin 编写,用于 Android 模块化开发的一整套解决方案框架。
android技术分享
2022/09/07
2K0
货拉拉 Android 模块化路由框架:TheRouter
有赞移动 iOS 组件化(模块化)架构设计实践
业务组件化(或者叫模块化)作为移动端应用架构的主流方式之一,近年来一直是业界积极探索和实践的方向。有赞移动团队自 16 年起也在不断尝试各种组件化方案,在有赞微商城,有赞零售,有赞美业等多个应用中进行了实践。我们踩过一些坑,也收获了很多宝贵的经验,并沉淀出 iOS 相关框架 Bifrost (雷神里的彩虹桥 https://github.com/youzan/Bifrost )。在过程中我们深刻体会到“没有绝对正确的架构,只有最合适的架构”这句话的意义。很多通用方案只是组件化的冰山一角,实际落地过程中还有相当多的东西需要考量。 本文并不准备对组件化架构设计方案给出一份标准答案,而是希望通过我们的实践经验和思考分析,提供一种思路,对遇到类似问题的同学能有所启发。
有赞coder
2020/08/25
1.3K0
有赞移动 iOS 组件化(模块化)架构设计实践
Android 大型工程 App Bundle 模块化实践
导言 Android App Bundle 是 Android 新推出的一种官方发布格式,可让您以更高效的方式开发和发布应用。企业微信基于 App Bundle 采用低入侵、业务代码基本零重构的技术方案,实现了全业务模块采用动态模块(dynamic feature)开发。最后展示并行编译方案,进一步加速持续集成。 一、项目背景 Android App Bundle 是 Android 新推出的一种官方发布格式,可让您以更高效的方式开发和发布应用。和国内开发者已经熟知的 Kotlin 开发语言、Androi
微信终端开发团队
2021/05/26
5.2K0
首次公开,最新手机QQ客户端架构的技术演进实践
接上篇《总是被低估,从未被超越,揭秘QQ极致丝滑背后的硬核IM技术优化》,本文则将重点介绍手机 QQ 客户端技术架构升级背后的故事。
JackJiang
2024/05/30
6490
首次公开,最新手机QQ客户端架构的技术演进实践
Android 模块化探索与实践
首发于《程序员》杂志五月刊 前言 万维网发明人 Tim Berners-Lee 谈到设计原理时说过:“简单性和模块化是软件工程的基石;分布式和容错性是互联网的生命。” 由此可见模块化之于软件工程领域的重要性。 从 2016 年开始,模块化在 Android 社区越来越多的被提及。随着移动平台的不断发展,移动平台上的软件慢慢走向复杂化,体积也变得臃肿庞大;为了降低大型软件复杂性和耦合度,同时也为了适应模块重用、多团队并行开发测试等等需求,模块化在 Android 平台上变得势在必行。阿里 Android
张磊BARON
2018/04/13
1.1K0
Android 模块化探索与实践
Android组件化架构实践
从工程领域来看,模块化、组件化、插件化三种技术都是指将复杂代码进行拆分,达到解偶分层、便于管理的目的。普遍意义上,将代码按照业务模块划分就是模块化,如果再进一步从模块化代码中抽出通用于所有App的组件,作为一个独立的module或者maven依赖(比如一些比较有名的第三方SDK),这个组件生成的过程就叫组件化。插件化则是指将App按一定规则拆分成几个若干个APK,除了主APK,其他APK均可以通过网络下发然后通过主APK加载。通过加载、修改、卸载非主APK,一定程度上给予了APP热修复的功能。然而随着Android 9.0上私有API的限制,插件化受到了极大的限制,主流方案慢慢向稳定、务实的的组件化方案演进。
Android技术干货分享
2019/04/19
2.7K0
Android组件化架构实践
Android模块化开发
随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多。此过程中,你是否有过以下烦恼?
android技术分享
2023/03/14
2.2K0
知乎 Android 客户端组件化实践
知乎 Android 客户端最早使用的是最常见的单工程 MVC 架构,所有业务逻辑都放在了主工程 Module 里,网络层和一些公共代码分别被抽成了一个 Module。现在看来,当时的业务线、产品功能及研发团队都比不上现在的体量和丰富度,遇到的问题随时组内沟通就可以解决。所以在知乎稳步发展的前几年,并没有遇到什么大的问题。
用户1269200
2018/10/25
2.2K0
知乎 Android 客户端组件化实践
美团外卖Android平台化架构演进实践
美团外卖自2013年创建以来,业务一直高速发展。目前美团外卖日完成订单量已突破1800万,成为美团点评最重要的业务之一。美团外卖的用户端入口,从单一的外卖独立App,拓展为外卖、美团、点评等多个App入口。美团外卖所承载的业务,也从单一的餐饮业务,发展到餐饮、超市、生鲜、果蔬、药品、鲜花、蛋糕、跑腿等十多个大品类业务。业务的快速发展对客户端架构不断提出新的挑战。 平台化背景 很早之前,外卖作为孵化中的项目只有美团外卖App(下文简称外卖App)一个入口,后来外卖作为一个子频道接入到美团App(下文简称外卖频
美团技术团队
2018/03/29
1.7K0
美团外卖Android平台化架构演进实践
使用ARouter进行Android模块化开发
关于为什么要进行Android模块化开发,网上也已经讲烂了,不过归结起来,大体是可以总结为:在App开发的初期,代码量不大,业务量比较少,一个App作为一个单独的模块进行开发,往往问题不大。但随着业务的增多,代码变的越来越复杂,每个模块之间的代码耦合变得越来越严重,结构越来越臃肿,修改一处代码要编译整个工程,导致非常耗时,这时候最好的办法就是进行模块化拆分。
Android技术干货分享
2019/07/10
1.5K0
企业微信 iOS 工程演进之路(一)- 组件化
导语:本文介绍了企业微信 iOS 端工程近一年的演进方向,随着业务快速迭代企业微信 iOS 端已经成长为一个大型项目,去年我们还接入了腾讯会议、腾讯文档、企业邮箱等插件,为了适应业务的高速发展,我们进行了组件化、插件集成能力建设、Bazel 迁移等工作。
腾讯TMF团队
2022/12/15
1.6K1
沪江学习Android端重构实践
前言 本篇是何梁伟的投稿~ 何梁伟,沪江高级Android工程师,在他的带领下,沪江学习对现有App进行了一次大范围重构,也是我们对项目架构的一次探索之旅~ 对于大的架构重构,其实我们一直很谨慎的。我们的原则是将重构融合在每次迭代中,逐步优化代码的结构。这次针对整个应用的架构的调整背景是,公司移动开发部门的人数和项目越来越多,当初设计的移动端的架构让项目的依赖关系越来越复杂,维护成本也越来越高。刚好赶上公司产品的特别需求,我们决定梳理并优化一下整个项目结构。在实施过程中,我们依然坚持将整个重构的过程
用户1907613
2018/07/20
3820
企业微信大型Android系统重构之路
很多人以为存在时间很长的就是遗留系统,但这其实是个误区。时间长短并不能作为衡量遗留系统的标准。判断遗留系统的几个维度是:代码、架构、测试、DevOps以及技术和工具。代码质量差、架构混乱、没有测试、纯手工的 DevOps(或运维)、老旧的技术和工具,才是遗留系统的真正特点。
腾讯技术工程官方号
2024/01/26
5160
企业微信大型Android系统重构之路
移动应用架构治理初探:从依赖分析与 Android 应用的生命周期说起
最近的项目比较忙,能腾出的业余时间不多。周内,“机缘巧合” 之下,与国内的某知名手机厂商的架构师们,一起聊了聊如何进行 Android 的架构治理,而其中的出发点是:如何从依赖治理的角度来进行 Android 的架构治理? 作为一个非常熟悉 Android 和 Harmony OS 依赖分析的、非专业移动应用开发者,我大抵还算是有一定的经验。先从结论来说,Android 应用与一般的 Web 应用存在诸多的差异,在分析方式上也存在比较大的区别。也因此,而如果没有足够的体量或者是数量,那么并不需要花费大量的时
Phodal
2022/08/25
5660
移动应用架构治理初探:从依赖分析与 Android 应用的生命周期说起
一种基于广播的模块化架构简单实现
相信不少读者在开发时都有这样的困扰,项目刚开始时,代码量少,效率还可以,可维护性也不错。但随着项目的迭代,添加了各种各样的需求后,代码日积月累臃肿不堪,软件效率开始变得低下,可维护性变差,最后甚至被新人各种吐槽,这时候软件架构就显得尤为重要了。架构是软件开发的基础,直接影响着软件运行效率、代码的可维护性、可扩展性以及可读性。开始新项目时,先设计一个好的架构往往会让往后的开发事半功倍。   软件架构指的是系统的一些列抽象,用于指导软件系统的开发,并且与具体的业务无关。软件架构通常也描述了系统各个元件之间的逻辑关系,这个逻辑关系也可以简单理解为模块化。本文将向读者推荐一种基于广播的模块化架构,以下简称架构。   本文不打算向读者介绍庞大成熟的架构设计,而是为读者提供一种快速简单有效的解决方案。如果你从来没有考虑过系统架构,希望通过本文能够让你重新思考这一基础问题。如果你对架构设计有一定的经验,也许也能够给你不一样的视角。
阿利民
2022/05/16
3370
一种基于广播的模块化架构简单实现
Android组件化专题 - 组件化配置
(摘自百度百科)模块化是指解决一个复杂的问题时自顶向下逐层把系统划分为若干个模块的过程,各个模块可独立工作。
用户3045442
2018/09/11
1.1K0
Android组件化专题 - 组件化配置
相关推荐
微信 Android 模块化架构重构实践(上)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档